将文件从程序集资源流写入磁盘

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(),并遇到很多“进程无法访问文件 因为它正在被另一个进程使用。”来自 ASP.NET 的错误(因为 Stream 仍处于打开状态)。我回去使用 usings 来清除它。 @eduncan911:使用 CopyTo 不会改变您是否需要 using 语句。它只是改变了在流打开时复制自身的过程。【参考方案3】:

如果这是您的问题,您将不得不编写一个循环。但是你可以不用 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+ 的必经之路。我还注意到创建FileStreams 的一种快速方法是使用File 对象上的静态方法,例如File.Create()【参考方案6】:

这对我有用:

    using (MemoryStream input = new MemoryStream(Properties.Resources.*RESOURCE_NAME*))
    using (Stream output = File.Create(databasePath))


                    input.CopyTo(output)
    

【讨论】:

以上是关于将文件从程序集资源流写入磁盘的主要内容,如果未能解决你的问题,请参考以下文章

wpf 程序集资源

将数据从缓冲区写入磁盘上的波形文件[关闭]

如何实现平台无关的异步写入文件?

WPF学习第二十八章 程序集资源

如何获取应用程序从文件系统读取/写入文件所花费的总时间?

从内存而不是磁盘加载.NET程序集[关闭]