2009-08-19 67 views
6

在我需要隐藏被服务给用户一个PDF文件的位置,我的asp.net web应用程序之一。试图流与asp.net一个PDF文件时产生“损坏的文件”

因此,我写检索来自它的一个CMS系统上的位置的二进制内容,然后刷新一个字节数组到网络用户的方法。

不幸的是,在下载流时出现错误:“无法打开文件,因为它被damadged”(或类似的东西,当在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(); 
    } 
+1

为什么不使用Response.WriteFile方法? – 2009-08-19 15:32:57

+0

该文件在文件系统中并不存在。 – Pablo 2009-08-19 15:34:50

+0

更加清楚:该文件存在于CMS数据库中。它是可寻址的url,但我无法真正从文件系统中检索文件。 – Pablo 2009-08-19 15:35:58

回答

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(); 
+0

不知道哪个代码使它工作,但它终于工作:) 谢谢! – Pablo 2009-08-25 08:21:20

+0

您是否将此与ReportViewer一起使用? – JoshYates1980 2016-03-28 20:20:24

+0

指定一定数量的字节的缺点是,即使文件不如此大,所有这些字节都将被写入流中。您只需获取流的长度,然后在调用reader时使用该数字.ReadBytes – 2017-10-24 08:13:42

0

我在当前的2.0网站上有类似的工作。我记得尽管这已经有一段时间了,但我仍然努力工作,所以我不记得那些挣扎。

有,不过,什么之间有一些差别我中有你有什么。希望这些能帮助你解决问题。

  • 在ClearContent调用之后,我调用ClearHeaders();
  • 我没有指定长度。
  • 我没有在处理中指定内联
  • 我知道每个人都说不要这样做,但我有一个Response.Flush();接着是Response.Close();

而且,另一件事情,请检查您的contentType值,以确保它是PDF文件正确((“应用程序/ PDF格式”)。

1

这个片段为我做:

   Response.Clear(); 
       Response.ClearContent(); 
       Response.ClearHeaders(); 
       Response.ContentType = mimeType; 
       Response.AddHeader("Content-Disposition", "attachment"); 
       Response.WriteFile(filePath); 
       Response.Flush(); 
6

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

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(); 
} 

由于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; 
     } 
    } 
} 

然后,您可以使用它像这样:

inputStream.CopyTo(response.OutputStream, contentLength); 

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

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

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

0

当你使用你的代码时,你直接在你的回应中复制你的pdf中的字节。所有的特殊ascii代码需要编码传入http。当使用流功能时,流被编码,所以你不必担心它。

var bytes = new byte[contentLen]; 
    stream.Read(bytes, 0, contentLen); 
    stream.Close(); 
    Response.BinaryWrite(bytes); 
0

这个片段包括从文件路径的文件阅读,并提取出该文件的名称代码:

private void DownloadFile(string path) 
{ 
    using (var file = System.IO.File.Open(path, FileMode.Open)) 
    { 
     var buffer = new byte[file.Length]; 
     file.Read(buffer, 0, (int)file.Length); 

     var fileName = GetFileName(path); 
     WriteFileToOutputStream(fileName, buffer); 
    } 
} 

private string GetFileName(string path) 
{ 
    var fileNameSplit = path.Split('\\'); 
    var fileNameSplitLength = fileNameSplit.Length; 
    var fileName = fileNameSplit[fileNameSplitLength - 1].Split('.')[0]; 
    return fileName; 
} 

private void WriteFileToOutputStream(string fileName, byte[] buffer) 
{ 
    Response.Clear(); 
    Response.ContentType = "application/pdf"; 
    Response.AddHeader("Content-Disposition", 
     $"attachment; filename\"{fileName}.pdf"); 
    Response.OutputStream.Write(buffer, 0, buffer.Length); 
    Response.OutputStream.Close(); 
    Response.Flush(); 
    Response.End(); 
} 
+0

然后它读取字节数组中的整个文件并保持文件打开。 – CodeCaster 2017-10-24 08:23:22

+0

你会推荐什么样的改变?欢呼 – 2017-10-24 08:31:57

+0

我是否也需要调用Response.OutputStream.Close()? – 2017-10-24 08:32:49