将文件从程序集资源流写入磁盘
Posted
技术标签:
【中文标题】将文件从程序集资源流写入磁盘【英文标题】:Write file from assembly resource stream to disk 【发布时间】:2022-01-20 10:22:19 【问题描述】:我似乎找不到比以下更有效的方法将嵌入式资源“复制”到磁盘:
using (BinaryReader reader = new BinaryReader(
assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext")))
using (BinaryWriter writer
= new BinaryWriter(new FileStream(path, FileMode.Create)))
long bytesLeft = reader.BaseStream.Length;
while (bytesLeft > 0)
// 65535L is < Int32.MaxValue, so no need to test for overflow
byte[] chunk = reader.ReadBytes((int)Math.Min(bytesLeft, 65536L));
writer.Write(chunk);
bytesLeft -= chunk.Length;
似乎没有更直接的方法来进行复制,除非我遗漏了什么......
【问题讨论】:
对我来说看起来不错。是不是感觉代码行数太多了? 感觉应该有比分块更直接的方式。 【参考方案1】:我个人会这样做:
using (BinaryReader reader = new BinaryReader(
assembly.GetManifestResourceStream(@"Namespace.Resources.File.ext")))
using (BinaryWriter writer
= new BinaryWriter(new FileStream(path, FileMode.Create)))
byte[] buffer = new byte[64 * 1024];
int numread = reader.Read(buffer,0,buffer.Length);
while (numread > 0)
writer.Write(buffer,0,numread);
numread = reader.Read(buffer,0,buffer.Length);
writer.Flush();
【讨论】:
我喜欢减去同花顺的方式,但不包括更直接的方式,我想我会以你的方式作为答案。【参考方案2】:我不知道你为什么要使用BinaryReader
/BinaryWriter
。就我个人而言,我会从一个有用的实用方法开始:
public static void CopyStream(Stream input, Stream output)
// Insert null checking here for production
byte[] buffer = new byte[8192];
int bytesRead;
while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
output.Write(buffer, 0, bytesRead);
然后调用它:
using (Stream input = assembly.GetManifestResourceStream(resourceName))
using (Stream output = File.Create(path))
CopyStream(input, output);
您当然可以更改缓冲区大小,或者将其作为方法的参数 - 但要点是这是更简单的代码。是不是更有效率?没有。你确定你真的需要这段代码来提高效率吗?您实际上需要将数百兆字节写入磁盘吗?
我发现我很少需要超高效的代码,但我几乎总是需要它简单。您可能会在这种方法和“聪明”方法(如果有的话)之间看到的那种性能差异不太可能是改变复杂性的效果(例如 O(n) 到 O(log n)) - 这就是真正值得追求的性能提升类型。
编辑:如 cmets 中所述,.NET 4.0 具有 Stream.CopyTo
,因此您无需自己编写代码。
【讨论】:
光荣,我想我已经成为忽略 Stream 类的牺牲品。可怜的,可怜的流。 有问题的文件在5-10MB之间,所以在速度方面可以忽略不计。我一直在寻找简单/简洁的东西(因为简单/简洁往往意味着高效)。 我尝试更改为 CopyTo(),并遇到很多“进程无法访问文件如果这是您的问题,您将不得不编写一个循环。但是你可以不用 reader 和 writer,因为基本 Stream 已经处理了 byte[] 数据。
这是我能得到的最紧凑的:
using (Stream inStream = File.OpenRead(inputFile))
using (Stream outStream = File.OpenWrite(outputFile))
int read;
byte[] buffer = new byte[64 * 1024];
while ((read = inStream.Read(buffer, 0, buffer.Length)) > 0)
outStream.Write(buffer, 0, read);
【讨论】:
【参考方案4】:如果资源(文件)是二进制的。
File.WriteAllBytes("C:\ResourceName", Resources.ResourceName);
如果资源(文件)是文本。
File.WriteAllText("C:\ResourceName", Resources.ResourceName);
【讨论】:
这是唯一合理的答案!其他人都在提供具有多个流、缓冲区和超过 10 行的荒谬构造,而这一行也是如此。 这种方法看起来至少是不完整的。 File.WriteAllBytes 的 .NET 4.0 文档表明第二个参数是一个字节数组,其内容要写入文件。 File.WriteAllText 的文档表明第二个参数是要写入文件的字符串。因此,将Resources.ResourceName
指定为第二个参数实际上没有任何意义。
如果Resources.ResourceName
是二进制,则Resources.ResourceName
的类型是Byte[]
,根据需要,它将按预期工作。这是否让您完整/清楚?
更好的答案。一行!
我感觉有点密集,因为其他人已经清楚地使用了这个解决方案。当我运行 File.WriteAllText(output_file, resource_name) 时,我当然会得到一个包含资源名称的文件。你在使用 System.Resources 命名空间吗?如果是这样,您如何通过此命名空间按名称引用特定的嵌入式程序集资源?这可能是 VB 而不是 C#?【参考方案5】:
我实际上最终使用了这一行:
Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(New FileStream(FileLocation, FileMode.Create))
。当然,这是针对 .Net 4.0
更新: 我发现上面的行可能会锁定一个文件,以便 SQLite 报告数据库是只读的。因此,我最终得到以下结果:
Using newFile As Stream = New FileStream(FileLocation, FileMode.Create)
Assembly.GetExecutingAssembly().GetManifestResourceStream("[Project].[File]").CopyTo(newFile)
End Using
【讨论】:
+1,绝对是 .Net 4.0+ 的必经之路。我还注意到创建FileStream
s 的一种快速方法是使用File
对象上的静态方法,例如File.Create()
。【参考方案6】:
这对我有用:
using (MemoryStream input = new MemoryStream(Properties.Resources.*RESOURCE_NAME*))
using (Stream output = File.Create(databasePath))
input.CopyTo(output)
【讨论】:
以上是关于将文件从程序集资源流写入磁盘的主要内容,如果未能解决你的问题,请参考以下文章