将文件压缩为字节以上载到SQL Server

时间:2010-04-16 20:38:02

标签: c# gzip

我正在尝试将文件压缩到SQL Server数据库表。我不能确保该工具的用户在源文件文件夹上写了priveledges,所以我想将文件加载到内存中,将其压缩为一个字节数组并将其插入我的数据库。

以下内容不起作用。

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;
            }
        }
    }
}

4 个答案:

答案 0 :(得分:7)

如果将代码拆分为较小的块,则代码将更容易调试。在我的例子中,我提供了一个压缩和解压缩方法。此外,您无需滚动自己的代码来读取FileStream中的所有字节。您只需使用File.ReadAllBytes即可。第三,确保在IDisposable语句中包装实现using的类。

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();
}

答案 1 :(得分:1)

根据docs

  

写入可能不会立即发生,但会被缓冲,直到达到缓冲区大小或直到调用Flush或Close方法。

因此,您可以尝试使用zip.Flush()来确保它刷新流。

此外,在将内存流传递到ReadWholeArray方法时,请确保通过将其Position属性设置为0来回滚该流。

答案 2 :(得分: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();

答案 3 :(得分:0)

提防。 MemoryStream将使用零填充输出数组。在调用ToArray()之前需要记住它的最终位置,并在数组后将数组截断为适当的大小。