簡體   English   中英

將文件壓縮為字節以上傳到SQL Server

[英]Compress file to bytes for uploading to SQL Server

我正在嘗試將文件壓縮到SQL Server數據庫表中。 我不能確保該工具的用戶在源文件文件夾上具有寫特權,因此我想將該文件加載到內存中,將其壓縮為字節數組並將其插入到我的數據庫中。

這在下面不起作用。

class ZipFileToSql
{
    public event MessageHandler Message;
    protected virtual void OnMessage(string msg)
    {
        if (Message != null)
        {
            MessageHandlerEventArgs args = new MessageHandlerEventArgs();
            args.Message = msg;
            Message(this, args);
        }
    }
    private int sourceFileId;
    private SqlConnection Conn;
    private string PathToFile;
    private bool isExecuting;
    public bool IsExecuting
    {
        get
        { return isExecuting; }
    }
    public int SourceFileId
    {
        get
        { return sourceFileId; }
    }
    public ZipFileToSql(string pathToFile, SqlConnection conn)
    {
        isExecuting = false;
        PathToFile = pathToFile;
        Conn = conn;
    }
    public void Execute()
    {
        isExecuting = true;
        byte[] data;
        byte[] cmpData;
        //create temp zip file
        OnMessage("Reading file to memory");
        FileStream fs = File.OpenRead(PathToFile);
        data = new byte[fs.Length];
        ReadWholeArray(fs, data);
        OnMessage("Zipping file to memory");
        MemoryStream ms = new MemoryStream();
        GZipStream zip = new GZipStream(ms, CompressionMode.Compress, true);
        zip.Write(data, 0, data.Length);
        cmpData = new byte[ms.Length];
        ReadWholeArray(ms, cmpData);
        OnMessage("Saving file to database");
        using (SqlCommand cmd = Conn.CreateCommand())
        {
            cmd.CommandText = @"MergeFileUploads";
            cmd.CommandType = CommandType.StoredProcedure;
            //cmd.Parameters.Add("@File", SqlDbType.VarBinary).Value = data;
            cmd.Parameters.Add("@File", SqlDbType.VarBinary).Value = cmpData;
            SqlParameter p = new SqlParameter();
            p.ParameterName = "@SourceFileId";
            p.Direction = ParameterDirection.Output;
            p.SqlDbType = SqlDbType.Int;
            cmd.Parameters.Add(p);
            cmd.ExecuteNonQuery();
            sourceFileId = (int)p.Value;
        }
        OnMessage("File Saved");
        isExecuting = false;
    }


    private void ReadWholeArray(Stream stream, byte[] data)
    {
        int offset = 0;
        int remaining = data.Length;
        float Step = data.Length / 100;
        float NextStep = data.Length - Step;
        while (remaining > 0)
        {
            int read = stream.Read(data, offset, remaining);
            if (read <= 0)
                throw new EndOfStreamException
                    (String.Format("End of stream reached with {0} bytes left to read", remaining));
            remaining -= read;
            offset += read;
            if (remaining < NextStep)
            {
                NextStep -= Step;
            }
        }
    }
}

如果將代碼分成較小的塊,則將更易於調試。 在我的示例中,我提供了一種Compress和Decompress方法。 另外,您無需滾動自己的代碼即可從FileStream讀取所有字節。 您可以簡單地使用File.ReadAllBytes 第三,確保在using語句中包裝實現IDisposable類。

public void Execute()
{
    isExecuting = true;
    byte[] data;
    byte[] cmpData;

    //create temp zip file
    OnMessage("Reading file to memory");

    byte[] data = File.ReadAllBytes(  PathToFile );

    OnMessage("Zipping file to memory");
    byte[] compressedData = Compress(data);

    OnMessage("Saving file to database");
    SaveToDatabase( compressedData );

    OnMessage("File Saved");
    isExecuting = false;
}

private void SaveToDatabase( byte[] data )
{
     using ( var cmd = Conn.CreateCommand() )
    {
        cmd.CommandText = @"MergeFileUploads";
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.AddWithValue("@File", data );
        cmd.Parameters["@File"].DbType = DbType.Binary;

        cmd.Parameters.Add("@SourceField");
        var parameter = cmd.Parameters["@SourceField"];
        parameter.DbType = DbType.Int32;
        parameter.Direction = ParameterDirection.Output;

        cmd.ExecuteNonQuery();
        sourceFileId = (int)parameter.Value;
    }
}

private static byte[] Compress( byte[] data )
{
    var output = new MemoryStream();
    using ( var gzip = new GZipStream( output, CompressionMode.Compress, true ) )
    {
        gzip.Write( data, 0, data.Length );
        gzip.Close();
    }
    return output.ToArray();
}
private static byte[] Decompress( byte[] data )
{
    var output = new MemoryStream();
    var input = new MemoryStream();
    input.Write( data, 0, data.Length );
    input.Position = 0;

    using ( var gzip = new GZipStream( input, CompressionMode.Decompress, true ) )
    {
        var buff = new byte[64];
        var read = gzip.Read( buff, 0, buff.Length );

        while ( read > 0 )
        {
            output.Write( buff, 0, read );
            read = gzip.Read( buff, 0, buff.Length );
        }

        gzip.Close();
    }
    return output.ToArray();
}

根據文檔

寫入可能不會立即發生,但是會被緩沖,直到達到緩沖區大小或調用Flush或Close方法為止。

因此,您可以嘗試放入zip.Flush()以確保其刷新流。

另外,將內存流傳ReadWholeArray方法時,請通過將其Position屬性設置為0來倒回該流。

您可能可以簡化執行壓縮和字節數組轉換的代碼,以便遵循以下內容(未經測試,但應該關閉)

  MemoryStream ms = new MemoryStream();
  using (FileStream fs = File.OpenRead(PathToFile))
  using (GZipStream zip = new GZipStream(ms, CompressionMode.Compress))
  {        
    // This could be replaced with fs.CopyTo(zip); if you are using Framework 4.0
    byte[] buffer = new byte[1024];
    int bytesRead = 0;
    while ((bytesRead = fs.Read(buffer, 0, buffer.Length)) > 0)
    {        
      zip.Write(buffer, 0, bytesRead);
    }
  }

  // Get the compressed bytes from the memmory stream
  byte[] cmpData = ms.ToArray();

謹防。 MemoryStream將用零填充您的輸出數組。 您需要記住其最終位置,然后再調用ToArray()並將數組截斷為適當大小的后綴。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM