如何在SQL Server数据库中将文件存储和检索为COMPRESSED二进制blob?

时间:2014-05-12 17:02:01

标签: sql-server vb.net

我在互联网上看到了很多示例代码,展示了如何在文件系统上压缩和解压缩文件,还有很多关于如何存储常规 SQL Server数据库中的文件,但从不同时。这是我到目前为止的代码。 (我在VB.NET中使用实体框架,但这不是重点。我希望。)

Private Sub ButtonStore_Click(sender As Object, e As EventArgs) Handles ButtonStore.Click

    Dim db As New Storage
    Dim bld = db.Builds.Find(1)
    Dim ecfg_file = New FileStream("D:\Temp\X1_450_1750_60207003.ecfg", FileMode.Open)
    Dim sr = New StreamReader(ecfg_file)
    Dim contents = sr.ReadToEnd

    Dim ms = New MemoryStream()
    Dim comp_stream = New GZipStream(ms, CompressionMode.Compress)
    Dim sw = New StreamWriter(comp_stream)
    sw.Write(contents)
    sw.Flush()
    comp_stream.Flush()
    ms.Flush()

    bld.EcfgFile = ms.ToArray()

    db.SaveChanges()
    db.Dispose()
    ecfg_file.Close()
    comp_stream.Close()
    ms.Close()

End Sub


Private Sub ButtonExtract_Click(sender As Object, e As EventArgs) Handles ButtonExtract.Click

    Dim db As New Storage
    Dim bld = db.Builds.Find(1)
    Dim ecfg_file = New FileStream("D:\Temp\asdf.ecfg", FileMode.Create)

    Dim ms As New MemoryStream()
    ms.Write(bld.EcfgFile.ToArray, 0, bld.EcfgFile.Length)
    ms.Position = 0
    Dim decompression_stream = New GZipStream(ms, CompressionMode.Decompress, True)
    Dim sr = New StreamReader(decompression_stream)
    Dim asdf = sr.ReadToEnd
    Dim sw = New StreamWriter(ecfg_file)
    sw.Write(asdf)

    ecfg_file.Close()
    decompression_stream.Close()
    ms.Close()

End Sub

这非常接近。问题是,在提取时,文件会覆盖自身的一部分,或者停止缩短。原始文件为18,613 KB,存储和提取的文件为18,446 KB。我甚至不知道在存储或提取过程中是否出现问题。

正如您所看到的,我正在尝试.Flush.Close()所有内容以确保所有内容都正确完成。 (是的,我可以尝试Using,但我不喜欢它最终会产生的所有缩进。)

2 个答案:

答案 0 :(得分:1)

简短回答:

您可能会发现CLR运行时函数,例如DeflateStreamthis article中的示例实现)和GZipStream非常有用。

答案很长:

我假设您将文件存储在varbinary(max)列中的数据库中,而且从Kornelis收集的内容中,SQL Server不能以您希望的方式压缩大对象(LOB)数据它与页面压缩(或行压缩,或列存储索引,我假设扩展到columnstore_archive)。作者基本上指出这是因为跨越多个页面的数据(当然LOB数据会自然会这样)不会受益于这种类型的压缩。

我正在寻找类似的解决方案,到目前为止,我倾向于使用CLR函数进行压缩,基本上在将数据写入表之前压缩数据,并在读取数据后进行解压缩。这会带来影响,但据我理解,就像无法在压缩数据上使用某些搜索功能一样 - 根据Henderson - 无法再对它进行异步读/写操作。

答案 1 :(得分:0)

Plutonix使用Using块清理对象是正确的,而Max Vernon可能对代码的可读性是正确的。

这有效:

Private Sub ButtonStore_Click(sender As Object, e As EventArgs) Handles ButtonStore.Click

    Dim contents As String

    Using ecfg_file = New FileStream("C:\Temp\X1_450_1750_60207003.ecfg", FileMode.Open)
        Using sr = New StreamReader(ecfg_file)
            contents = sr.ReadToEnd
        End Using
    End Using

    Using ms = New MemoryStream()
        Using comp_stream = New GZipStream(ms, CompressionMode.Compress)
            Using sw = New StreamWriter(comp_stream)
                sw.Write(contents)
            End Using
        End Using
        Using db As New Storage
            Dim bld = db.Builds.First
            bld.EcfgFile = ms.ToArray()
            db.SaveChanges()
        End Using
    End Using

    Debug.Print("Done")

End Sub


Private Sub ButtonExtract_Click(sender As Object, e As EventArgs) Handles ButtonExtract.Click

    Dim contents As String

    Using db As New Storage
        Dim bld = db.Builds.First
        Using ms = New MemoryStream()
            ms.Write(bld.EcfgFile.ToArray, 0, bld.EcfgFile.Length)
            ms.Position = 0
            Using decompression_stream = New GZipStream(ms, CompressionMode.Decompress, True)
                Using sr = New StreamReader(decompression_stream)
                    contents = sr.ReadToEnd
                End Using
            End Using
        End Using
    End Using

    Using ecfg_file = New FileStream("C:\Temp\asdf.ecfg", FileMode.Create)
        Using sw = New StreamWriter(ecfg_file)
            sw.Write(contents)
        End Using
    End Using

    Debug.Print("Done")

End Sub