zoukankan      html  css  js  c++  java
  • FileStreamResult 下载或导出文件

    FileStreamResult的一个常用构造函数:

    public virtual FileStreamResult File(Stream fileStream, string contentType, string fileDownloadName);

    下面通过循序渐进的方式 讲述 项目中的实际应用。

    1. using块中的流

                image.png

        按照良好的编程习惯,将stream放在using块中,以确保它被释放。结果:

         fail : An unhandled exception has occurred while executing the request.

                System.ObjectDisposedException: Cannot access a closed Stream.

        原因在于 using块一执行完stream就被关闭了,view无法读取已关闭的流。

        但,流总不能不关闭。难道,有谁替我们做了这些?

        是的,FileStreamResult 自动做了这些,下面是反编译的源码

        

    protected override void WriteFile(HttpResponseBase response) {
        // grab chunks of data and write to the output stream
        Stream outputStream = response.OutputStream;
        using (FileStream) {
            byte[] buffer = new byte[_bufferSize];
    
            while (true) {
                int bytesRead = FileStream.Read(buffer, 0, _bufferSize);
                if (bytesRead == 0) {
                    // no more data
                    break;
                }
    
                outputStream.Write(buffer, 0, bytesRead);
            }
        }
    }

        删掉using后的代码如下:

    image.png

        运行成功,文件保存至本地。

    image.png

    2. 注意流的当前位置

    有些时候我们所调用的组件恰好收集了流数据,要利用这个流。

    下面的例子 通过 EPPlus组件创建Excel数据(须引用 EPPlus.dll 并 using OfficeOpenXml;)

    image.png

        上述代码将ExcelPackage的数据流直接作为即将被下载的文件的数据流而输出。看起来很完美,结果

         fail : An unhandled exception was thrown by the application.

                System.InvalidOperationException: Response Content-Length mismatch: too few bytes written <0 of 2546>

        原因在于stream的当前位置在末尾。

        常见的相同状况的还有 Stream.CopyTo() 。微软对此有说明:

        image.png

        添加一行代码 stream.Position = 0 以重置流的当前位置至开始。

    image.png

        运行成功,保存至本地的文件如下:

        image.png

    3. 压缩流

    上述2中的例子,文件很小,只简单写了两个单元格。如果文件大,则需要压缩,供客户端下载压缩包。

    有些同学真的是先创建文件保存至磁盘某路径A,然后压缩那个文件至路径B,最终将B返回。

    还没完,因为还得善后:清理掉文件A及压缩包B。
    我倾向于:不存磁盘,直接利用流。其实在上面的第2小节中的示例已经体现。

    下面的示例须添加 using System.IO.Compression

    image.png

    用于创建压缩包的流stream,即是将来要返回的流。

    向压缩包中添加的entry并写入数据, 用的entryStream,也是流。这个entry是一个Excel文件,恰好由EPPlus组件的流填充。

    换一种说法:
    ExcelPackage的流,构成 System.IO.Compression某个ZipArchiveEntry的流,一个或多个ZipArchiveEntry流 构成了ZipArchive流,而后 ZipArchive流构成FileStreamResult中的流 供返回。

    全程都是流之间的流转,没有写磁盘,用不着去清理什么过程文件。

    美美的运行一下:oops,     fail : Cannot access a closed Stream.

    我并没有将 MemoryStream stream = new MemoryStream() 放入using块啊,是哪里关闭了它?

    查帮助文档,微软说:ZipArchive.Dispose() 此方法完成写入存档并释放由该ZipArchive对象使用的所有资源

    那就不把 ZipArchive archive = ... 放入using块呗,这样就不调用 ZipArchive.Dispose() 就不会关闭(释放)流了。

    新的代码如下:

     image.png

        运行成功,Happy
        dialog.png

        解压看看:
        image.png

        重点字眼“末端”,说明 不是写excel数据的问题。

        结合“ZipArchive.Dispose() 此方法完成写入存档并释放” ,第一感觉是 没有完成存档,之前关注点都放在释放上了。

        仔细阅读微软的说明:
    image.png

        就是说,必须得调用ZipArchive.Dispose() 以完成存档。但是,leaveOpen=true 才能保证流不关闭。

        image.png

        这次是真的运行成功了:

        image.png

    慎于行,敏于思!GGGGGG
  • 相关阅读:
    php 表单的活用
    PHP 内存的分布问题
    php 半角与全角相关的正则
    解决 U盘安装Windows Server 2012 R2 报错 Windows 无法打开所需的文件 Sourcesinstall.wim
    VS2010或2012中,如何设置代码格式化?
    变色龙引导安装黑苹果 遇到的问题的解决办法
    Ozmosis实现BIOS直接启动Yosemite,基本完美
    MMTool制作Ozmosis引导BIOS完美引导OS X系统
    黑苹果安装步骤
    win8.1 usb3 速度慢的解决方法
  • 原文地址:https://www.cnblogs.com/GarsonZhang/p/13037359.html
Copyright © 2011-2022 走看看