Сжать файл в байтах для загрузки на SQL Server - PullRequest
0 голосов
/ 17 апреля 2010

Я пытаюсь заархивировать файлы в таблицу базы данных 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;
            }
        }
    }
}

Ответы [ 4 ]

7 голосов
/ 17 апреля 2010

Ваш код будет легче отлаживать, если разбить его на более мелкие куски. В моем примере я предоставил метод сжатия и распаковки. Кроме того, вам не нужно накатывать собственный код для чтения всех байтов из 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 голос
/ 17 апреля 2010

Согласно документам :

Запись может происходить не сразу, но буферизируется до тех пор, пока не будет достигнут размер буфера или пока не будут вызваны методы Flush или Close.

Так что вы можете попробовать поставить zip.Flush(), чтобы убедиться, что он очищает поток.

Кроме того, при передаче потока памяти в метод ReadWholeArray убедитесь, что вы перематываете поток, установив для свойства Position значение 0.

0 голосов
/ 17 апреля 2010

BEWARE. MemoryStream заполнит ваш выходной массив нулями. Вам нужно запомнить его конечную позицию перед вызовом ToArray () и обрезать массив до нужного размера после слов.

0 голосов
/ 17 апреля 2010

Возможно, вы могли бы упростить ваш код, который выполняет сжатие и преобразование байтового массива, чтобы что-то вроде следующего (не проверено, но должно быть близко)

  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();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...