【发布时间】:2018-08-28 09:59:17
【问题描述】:
我在数据库中有许多文件(PDF、doc、docx、jpg 等),它们是使用以下方法添加的:
附件以 MIME 编码(base64 编码)发送到数据库 字符串。然后组件转换这个 MIME 编码的字符串(在 unicode) 到字节流,然后将其作为 BLOB 写入数据库 (Oracle) 或映像 (SQL Server)。
字符串开头还附加了一个“guid”,长度为 76 个字符。
我正在尝试提取附件并将其保存到磁盘上的文件中,而不是数据库中。它的工作时间约为 20%。在将我的字符串传递给FromBase64String 时,我得到System.FormatException: Invalid character in a Base-64 string. 的其余时间。
我注意到数据库中的值如下所示,保存成功:
0x7B00350030003100460032003300350046002D00370
失败的总是这样开始的:
0x7B35303146323335462D373546302D343936342D394
我在这里没有足够的字符来粘贴完整的示例,因此请参阅this pastebin 以获取不起作用的示例。它应该代表一个 Word 文档,上面写着“仅测试文档”。 This one 是同一个文档,但转换为 PDF。
This 可以工作并转换为test font.htm。它必须插入到 SQL 数据库中的 image 列中,然后用我的代码拉出:
private const int guidLength = 38 * 2;
public static byte[] GetAttachment(string folderid, string filename) {
string queryString = string.Format("SELECT <image column> FROM AttachmentTable WHERE .....",
folderid, filename);
using (SqlConnection connection = new SqlConnection("context connection=true"))
{
connection.Open();
using (SqlCommand selectAttachment = new SqlCommand(
queryString,
connection))
{
using (SqlDataReader reader = selectAttachment.ExecuteReader()) {
while (reader.Read())
{
if (reader[0] == System.DBNull.Value)
return new byte[0];
byte[] data = (byte[])reader[0];
byte[] truncatedData;
if (data[data.Length - 2] == 0)
truncatedData = new byte[data.Length - guidLength - 2];
else
truncatedData = new byte[data.Length - guidLength];
Array.Copy(data, guidLength, truncatedData, 0, truncatedData.Length);
// base64 unencode
string truncatedString = Encoding.Unicode.GetString(truncatedData);
return Convert.FromBase64String(truncatedString);
}
}
}
}
}
然后保存附件:
public static void SaveAttachmentToFile(string file, string folderid, string fileName)
{
byte[] data = GetAttachment(file, folderid);
if (data == null)
throw new ArgumentNullException("Attachment has no data, it may have been deleted");
using (FileStream writer = new FileStream(fileName, FileMode.Create))
{
writer.Write(data, 0, data.Length);
}
}
SQL CLR 函数
[SqlFunction(IsDeterministic = true,
IsPrecise = true,
DataAccess = DataAccessKind.Read,
SystemDataAccess = SystemDataAccessKind.Read)]
public static SqlString WriteToFile(SqlString path, SqlString folderid, SqlString fileName)
{
try
{
if (!path.IsNull && !folderid.IsNull && !fileName.IsNull)
{
var dir = Path.GetDirectoryName(path.Value);
if (!Directory.Exists(dir))
Directory.CreateDirectory(dir);
string filename = Convert.ToString(fileName);
string folderid = Convert.ToString(efolderid);
string filepath = Convert.ToString(path);
SaveAttachmentToFile(filename, folderid, filepath);
return "Wrote file";
}
else
return "No data passed to method!";
}
catch (IOException e)
{
return "Make sure the assembly has external access!\n" + e.ToString();
}
catch (Exception ex)
{
return ex.ToString();
}
}
注意,上面所有的 C# 代码都被编译成一个程序集,然后用作 CLR 函数:
CREATE FUNCTION [dbo].[WriteToFile](@path [nvarchar](max), @efolderid [nvarchar](max), @filename [nvarchar](max))
RETURNS [nvarchar](max) WITH EXECUTE AS CALLER
AS
EXTERNAL NAME [ClassLibrary1].[CLR.UserDefinedFunctions].[WriteToFile]
GO
我认为我的问题可能与编码有关。我以为我可以使用Encoding.MIME.GetString,但它不存在。我也尝试过UTF-8,但成功率为 0%。 Unicode 似乎可以工作,但如上所述,成功率约为 20%。
我的问题是,为什么其中一些无法保存(不正确的 base64 字符.. 但为什么?)而其他的却可以正常工作?如何确定要使用的正确编码?它有一个模式,但我真的不知道如何从这里开始。
【问题讨论】:
-
那些对我来说看起来不像 base64 字符串。它们看起来像是由十六进制对表示的字节序列。
-
也许,但其中一些确实转换为 base64,然后我可以将文件保存到磁盘
-
这并不意味着您最终会得到任何类型的有效数据。尝试在开始时截断 0x,然后每两个迭代它们并将它们从十六进制字符串转换为字节值,并保存生成的字节数组。
-
@Nyerguds Nope - 不太确定如何处理。你对如何实现
byte value from hex string部分有一个粗略的想法,比如,它是一个内置的.net 函数还是我需要自己编写的东西? -
你的问题有点不清楚,“0x”格式只是你用来检查数据库的任何东西显示的数据,而不是你实际的得到程序中的数据。虽然通过仔细阅读代码本身肯定可以推断出这一点,所以我为我的粗鲁道歉。