如何使用 MySQL 和 C#(MySQL 连接器)正确处理大 blob
How to work with big blobs properly, using MySQL and C# (MySQL Connector)
我需要 save/load 文件 to/from MySQL 数据库。估计文件大小约为 500 Mb。我没有太多使用 MySQL 的经验,也从未在数据库上下文中处理过这么大的文件。我的第一步是阅读 "MySQL Connector/Net Developer Guide",但那里的示例不太适合实际使用。我在这里寻找问题和答案,但我发现的所有关于 blob 的问题都是关于在 db 中保存图片。图片不是很大的文件,远小于 500Mb。
这里的例子,来自"MySQL Connector/Net Developer Guide",我说的是:
Writing BLOB to datavase example
Reading BLOB from database example
这是我根据这些示例编写的代码(在数据库中写入 blob):
public static class TABLES
{
public struct Column
{
public string Name { get; }
public string Table { get; }
public string Alias
{
get { return Table + Name; }
}
public string AS
{
get { return string.Format ( "{0} AS {1}", Name, Alias ); }
}
public string TableDotName
{
get { return string.Format ( "{0}.{1}", Table, Name ); }
}
public string TableDotAS
{
get { return string.Format ( "{0}.{1}", Table, AS ); }
}
public override string ToString ( )
{
return Name;
}
public Column ( string parTitle, string parTable )
{
Name = parTitle;
Table = parTable;
}
}
public class FILES
{
public const string TABLE = "Files";
public static readonly Column ID = new Column( "id", TABLE );
public static readonly Column TITLE = new Column( "Title", TABLE );
public static readonly Column FILE = new Column( "File", TABLE );
public static readonly Column [] COLUMNS = { ID, TITLE, FILE };
}
}
public bool AddFileParameter(MySqlCommand parCommand, string parFilePath, string parParameterName, bool parIsRequired )
{
bool retResult = false;
if(parCommand == null )
{
throw new NullReferenceException ( "MySqlCommand is null" );
}
if (System.IO.File.Exists( parFilePath ) )
{
using ( System.IO.FileStream fs = new System.IO.FileStream ( parFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read ) )
{
uint FileSize = (uint)fs.Length;
byte [ ] rawData = new byte[FileSize];
fs.Read ( rawData, 0, ( int ) FileSize );
fs.Close ( );
parCommand.Parameters.AddWithValue ( parParameterName, rawData );
retResult = true;
}
}
else if (parIsRequired)
{
throw new System.IO.FileNotFoundException ( "File not found", parFilePath );
}
return retResult;
}
public int InsertFile ( string parTitle, string parFilePath )
{
int retResult = 0;
string queryParTitle = "@Title";
string queryParFile = "@File";
string commandText = string.Format(@"INSERT IGNORE INTO {0} ({1}, {2}) VALUES({3}, {4});",
new object[] {
TABLES.FILES.TABLE, //0
TABLES.FILES.TITLE.Name, //1
TABLES.FILES.FILE.Name, //2
queryParTitle, //3
queryParFile, //4
} );
try
{
using ( var myConnection = DBOpenConnection ( ) )
{
try
{
using ( MySqlCommand myCommand = new MySqlCommand ( commandText, myConnection ) )
{
myCommand.Prepare ( );
myCommand.Parameters.AddWithValue ( queryParTitle, parTitle );
AddFileParameter ( myCommand, parFilePath, queryParFile, false );
retResult = DBExecuteNonQuery ( myCommand );
}
}
finally
{
DBCloseConnection ( myConnection );
}
}
return retResult;
}
catch ( DBConnectException e )
{
throw e;
}
catch ( DBDisconnectException e )
{
throw e;
}
catch ( DBExecuteQueryException e )
{
throw e;
}
catch ( DBReadValueException e )
{
throw e;
}
catch ( Exception e )
{
throw ( new DBException ( "Unhadled database error", e ) );
}
}
它可以工作,但问题是文件已完全加载到 RAM 中。而且我不知道如何逐个上传文件。
第二个问题是从数据库中获取文件。在上面的例子中,
我们必须明确指定 BLOB 的大小。这是一个耻辱。我怎么知道它的大小?为什么数据库不知道呢? MySQL 连接器有方法 "IsDBNull",但我找不到类似 "GetSize" 的方法。 BLOB 再次完全加载到 RAM 中。
那么,我的问题是:如何正确处理大 BLOB?如何逐段下载和上传?以及如何在不知道文件大小的情况下从 BLOB 获取文件。
how to upload file piece by piece?
您可以创建第二个 table 并将文件内容存储在那里,但您需要将每一行设置为具有最大字节数。像这样:
---------- -------------------------------------
|_IDS____| |_FileId__|_FileData__|_DataLength__|
|fileId1 | | fileId1 | 01010111 | 8 |
|fileId2 | | fileId1 | 11101111 | 8 |
---------- | fileId1 | 001001 | 6 |
| fileId2 | 00001 | 5 |
-------------------------------------
In the example above, we must explicitly specify the size of the BLOB. How should I know it's size?
有两种方法:
1. 将文件数据存储在某处:
var buffer = new byte[long.MaxValue];
var actualNumberOfBytesRead = reader.GetBytes(columnIndex, 0, buffer, 0, buffer.Length);
- 通过SQL:
SELECT OCTET_LENGTH(yourBlob) FROM yourTable;
但请注意 mysql 执行计算以检查大小。所以如果你查询尺寸,你的查询可能会很慢。
How to download and upload it piece by piece?
将 System.IO.FileStream class 与方法 Read() 和 Write() 以及我上面建议的 table 配置一起使用。
.
我需要 save/load 文件 to/from MySQL 数据库。估计文件大小约为 500 Mb。我没有太多使用 MySQL 的经验,也从未在数据库上下文中处理过这么大的文件。我的第一步是阅读 "MySQL Connector/Net Developer Guide",但那里的示例不太适合实际使用。我在这里寻找问题和答案,但我发现的所有关于 blob 的问题都是关于在 db 中保存图片。图片不是很大的文件,远小于 500Mb。 这里的例子,来自"MySQL Connector/Net Developer Guide",我说的是:
Writing BLOB to datavase example
Reading BLOB from database example
这是我根据这些示例编写的代码(在数据库中写入 blob):
public static class TABLES
{
public struct Column
{
public string Name { get; }
public string Table { get; }
public string Alias
{
get { return Table + Name; }
}
public string AS
{
get { return string.Format ( "{0} AS {1}", Name, Alias ); }
}
public string TableDotName
{
get { return string.Format ( "{0}.{1}", Table, Name ); }
}
public string TableDotAS
{
get { return string.Format ( "{0}.{1}", Table, AS ); }
}
public override string ToString ( )
{
return Name;
}
public Column ( string parTitle, string parTable )
{
Name = parTitle;
Table = parTable;
}
}
public class FILES
{
public const string TABLE = "Files";
public static readonly Column ID = new Column( "id", TABLE );
public static readonly Column TITLE = new Column( "Title", TABLE );
public static readonly Column FILE = new Column( "File", TABLE );
public static readonly Column [] COLUMNS = { ID, TITLE, FILE };
}
}
public bool AddFileParameter(MySqlCommand parCommand, string parFilePath, string parParameterName, bool parIsRequired )
{
bool retResult = false;
if(parCommand == null )
{
throw new NullReferenceException ( "MySqlCommand is null" );
}
if (System.IO.File.Exists( parFilePath ) )
{
using ( System.IO.FileStream fs = new System.IO.FileStream ( parFilePath, System.IO.FileMode.Open, System.IO.FileAccess.Read ) )
{
uint FileSize = (uint)fs.Length;
byte [ ] rawData = new byte[FileSize];
fs.Read ( rawData, 0, ( int ) FileSize );
fs.Close ( );
parCommand.Parameters.AddWithValue ( parParameterName, rawData );
retResult = true;
}
}
else if (parIsRequired)
{
throw new System.IO.FileNotFoundException ( "File not found", parFilePath );
}
return retResult;
}
public int InsertFile ( string parTitle, string parFilePath )
{
int retResult = 0;
string queryParTitle = "@Title";
string queryParFile = "@File";
string commandText = string.Format(@"INSERT IGNORE INTO {0} ({1}, {2}) VALUES({3}, {4});",
new object[] {
TABLES.FILES.TABLE, //0
TABLES.FILES.TITLE.Name, //1
TABLES.FILES.FILE.Name, //2
queryParTitle, //3
queryParFile, //4
} );
try
{
using ( var myConnection = DBOpenConnection ( ) )
{
try
{
using ( MySqlCommand myCommand = new MySqlCommand ( commandText, myConnection ) )
{
myCommand.Prepare ( );
myCommand.Parameters.AddWithValue ( queryParTitle, parTitle );
AddFileParameter ( myCommand, parFilePath, queryParFile, false );
retResult = DBExecuteNonQuery ( myCommand );
}
}
finally
{
DBCloseConnection ( myConnection );
}
}
return retResult;
}
catch ( DBConnectException e )
{
throw e;
}
catch ( DBDisconnectException e )
{
throw e;
}
catch ( DBExecuteQueryException e )
{
throw e;
}
catch ( DBReadValueException e )
{
throw e;
}
catch ( Exception e )
{
throw ( new DBException ( "Unhadled database error", e ) );
}
}
它可以工作,但问题是文件已完全加载到 RAM 中。而且我不知道如何逐个上传文件。
第二个问题是从数据库中获取文件。在上面的例子中, 我们必须明确指定 BLOB 的大小。这是一个耻辱。我怎么知道它的大小?为什么数据库不知道呢? MySQL 连接器有方法 "IsDBNull",但我找不到类似 "GetSize" 的方法。 BLOB 再次完全加载到 RAM 中。
那么,我的问题是:如何正确处理大 BLOB?如何逐段下载和上传?以及如何在不知道文件大小的情况下从 BLOB 获取文件。
how to upload file piece by piece?
您可以创建第二个 table 并将文件内容存储在那里,但您需要将每一行设置为具有最大字节数。像这样:
---------- ------------------------------------- |_IDS____| |_FileId__|_FileData__|_DataLength__| |fileId1 | | fileId1 | 01010111 | 8 | |fileId2 | | fileId1 | 11101111 | 8 | ---------- | fileId1 | 001001 | 6 | | fileId2 | 00001 | 5 | -------------------------------------
In the example above, we must explicitly specify the size of the BLOB. How should I know it's size?
有两种方法:
1. 将文件数据存储在某处:
var buffer = new byte[long.MaxValue]; var actualNumberOfBytesRead = reader.GetBytes(columnIndex, 0, buffer, 0, buffer.Length);
- 通过SQL:
SELECT OCTET_LENGTH(yourBlob) FROM yourTable;
但请注意 mysql 执行计算以检查大小。所以如果你查询尺寸,你的查询可能会很慢。How to download and upload it piece by piece?
将 System.IO.FileStream class 与方法 Read() 和 Write() 以及我上面建议的 table 配置一起使用。 .