尝试使用asp.net流式传输PDF文件会产生"损坏的文件"

Pab*_*blo 7 asp.net filestream

在我的一个asp.net Web应用程序中,我需要隐藏正在向用户提供的pdf文件的位置.

因此,我正在编写一种方法,从CMS系统上的位置检索其二进制内容,然后将字节数组刷新到Web用户.

不幸的是,我在下载流时遇到错误:"无法打开文件,因为它已被删除"(或类似于在adobe reader中打开文件时).

问题1:我做错了什么?问题2:我可以使用这种方法下载大文件吗?

    private void StreamFile(IItem documentItem)
    {
        //CMS vendor specific API
        BinaryContent itemBinaryContent = documentItem.getBinaryContent();
        //Plain old .NET
        Stream fileStream = itemBinaryContent.getContentStream();
        var len = itemBinaryContent.getContentLength();
        SendStream(fileStream, len, itemBinaryContent.getContentType());
    }

    private void SendStream(Stream stream, int contentLen, string contentType)
    {
        Response.ClearContent();
        Response.ContentType = contentType;
        Response.AppendHeader("content-Disposition", string.Format("inline;filename=file.pdf"));
        Response.AppendHeader("content-length", contentLen.ToString());
        var bytes = new byte[contentLen];
        stream.Read(bytes, 0, contentLen);
        stream.Close();
        Response.BinaryWrite(bytes);
        Response.Flush();
    }
Run Code Online (Sandbox Code Playgroud)

JJO*_*JJO 7

这是我使用的方法.这会传回一个附件,因此IE会生成一个打开/保存对话框.我也碰巧知道文件不会大于1M,所以我相信有更简洁的方法可以做到这一点.

我对PDF有类似的问题,我意识到我绝对不得不使用二进制流和ReadBytes.任何有字符串的东西搞砸了.

Stream stream = GetStream(); // Assuming you have a method that does this.
BinaryReader reader = new BinaryReader(stream);

HttpResponse response = HttpContext.Current.Response;
response.ContentType = "application/pdf";
response.AddHeader("Content-Disposition", "attachment; filename=file.pdf");
response.ClearContent();
response.OutputStream.Write(reader.ReadBytes(1000000), 0, 1000000);

// End the response to prevent further work by the page processor.
response.End();
Run Code Online (Sandbox Code Playgroud)


Ext*_*ank 7

其他答案在发送响应之前将文件内容复制到内存中.如果数据已经在内存中,那么您将拥有两个副本,这对可伸缩性来说不是很好.这可能更好地工作:

public void SendFile(Stream inputStream, long contentLength, string mimeType, string fileName)
{
    string clength = contentLength.ToString(CultureInfo.InvariantCulture);
    HttpResponse response = HttpContext.Current.Response;
    response.ContentType = mimeType;
    response.AddHeader("Content-Disposition", "attachment; filename=" + fileName);
    if (contentLength != -1) response.AddHeader("Content-Length", clength);
    response.ClearContent();
    inputStream.CopyTo(response.OutputStream);
    response.OutputStream.Flush();
    response.End();
}
Run Code Online (Sandbox Code Playgroud)

由于Stream是字节的集合,因此不需要使用BinaryReader.只要输入流在文件末尾结束,您就可以在要发送到Web浏览器的流上使用CopyTo()方法.所有内容都将写入目标流,而不会制作任何数据的中间副本.

如果只需要从流中复制一定数量的字节,则可以创建一个扩展方法,以添加更多的CopyTo()重载:

public static class Extensions
{
    public static void CopyTo(this Stream inStream, Stream outStream, long length)
    {
        CopyTo(inStream, outStream, length, 4096);
    }

    public static void CopyTo(this Stream inStream, Stream outStream, long length, int blockSize)
    {
        byte[] buffer = new byte[blockSize];
        long currentPosition = 0;

        while (true)
        {
            int read = inStream.Read(buffer, 0, blockSize);
            if (read == 0) break;
            long cPosition = currentPosition + read;
            if (cPosition > length) read = read - Convert.ToInt32(cPosition - length);
            outStream.Write(buffer, 0, read);
            currentPosition += read;
            if (currentPosition >= length) break;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它:

inputStream.CopyTo(response.OutputStream, contentLength);
Run Code Online (Sandbox Code Playgroud)

这适用于任何输入流,但一个简单的例子是从文件系统中读取文件:

string filename = @"C:\dirs.txt";
using (FileStream fs = File.Open(filename, FileMode.Open))
{
    SendFile(fs, fs.Length, "application/octet-stream", filename);
}
Run Code Online (Sandbox Code Playgroud)

如前所述,请确保您的MIME类型对于内容是正确的.