我正在尝试将文件压缩到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;
}
}
}
}
答案 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()之前需要记住它的最终位置,并在数组后将数组截断为适当的大小。