提示文件下载

22

我的网页上有一个链接,点击后我正在尝试生成一个PDF文档,并在浏览器上显示“打开-保存”提示框。

我的HTML(ReactJS组件)具有以下代码,其中onclick调用_getMyDocument函数,该函数然后调用Webapi方法。

 <div className="row">
     <a href="#" onClick={this._getMyDocument.bind(this)}>Test Link</a>
 </div>  

_getMyDocument(e) {
            GetMyDocument(this.props.mydata).then(()=> {
   }).catch(error=> {

  });

我的控制器有以下代码

[HttpPost]
[Route("Generate/Report")]
public IHttpActionResult GetMyReport(MyData myData)
 {  
    byte[] myDoc = MyBusinessObject.GenerateMyReport(myData);
    var result = new HttpResponseMessage(HttpStatusCode.OK)
        {
            Content = new ByteArrayContent(myDoc)
        };
        result.Content.Headers.ContentDisposition =
            new ContentDispositionHeaderValue("attachment")
            {
                FileName = "MyDocument.pdf"
            };
        result.Content.Headers.ContentType =
            new MediaTypeHeaderValue("application/octet-stream");

        var response = ResponseMessage(result);

        return response;
  }

目前所有代码都在执行,但我没有收到PDF文件下载提示。我在这里做错了什么?

从ajax调用成功的响应对象如下所示

输入图像描述


你试过我的解决方案了吗?请给我反馈。 - ScrapCode
我尝试按照@Ermir Beqiraj的答案分配下载URL。 var file = new Blob([response], { type: 'application/pdf' }); var fileURL = URL.createObjectURL(file); window.location = fileURL; 这将下载一个PDF文件,但是无效。 - San
MyBusinessObject.GenerateMyReport(myData); 是在做什么?它是否正在向另一个应用程序发出请求? - Brett Caswell
它调用另一个库(内部)来使用Microsoft Report Viewer生成报表数据(字节数组)。 - San
5个回答

9

你从服务器收到的响应看起来很好。缺失的部分是你没有以正确的方式处理客户端的响应。

假设您的js中的资源url对象如下所示。(即,您已经知道资源URL,如果您还没有了解它,则需要单独调用服务器以了解下载URL)

response.downloadUrl = app/media/fileId/document.pdf

你只需要设置,
window.location = item.downloadUrl;

这将导致浏览器向服务器请求资源,服务器的响应必须包括Content-Disposition:attachment;。这会导致浏览器显示下载对话框。
P.S. 我最近处理了类似的功能。如果你有问题,请问我。
当您想强制浏览器显示某些文件(或资源)的下载提示时,您必须在响应头中包含Content-Disposition:attachment;(您已经这样做了)。

我正在使用Microsoft报告查看器生成pdf。没有实际文件存在以设置下载url。我已更新我的问题,包括响应对象。 - San
@San,下载链接指的是任何能够返回PDF响应的Web服务地址(服务器请求)。因此,如果调用Microsoft报表查看器返回PDF,则需要将相同的调用请求分配给变量window.location - ScrapCode
我已经通过以下方式分配了URL -> var file = new Blob([response], { type: 'application/pdf' }); var fileURL = URL.createObjectURL(file) - San

2
在我们的应用程序(Angular)中,我们需要使用以下代码创建一个对象URL:
WebApi:
result = Request.CreateResponse(HttpStatusCode.OK);
result.Content = new ByteArrayContent(data);
result.Content.Headers.Add("Content-Type", "application/pdf");
result.Content.Headers.ContentDisposition =
                        new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
                        {
                            FileName = "FileNameHere"
                        };

return result;

JavaScript:

// HttpCall in here
// On SuccessResponse
    var file = new Blob([data], {
                    type: 'application/pdf'
                    });
    var fileURL = URL.createObjectURL(file);
// create an anchor and click on it.
    var anchorTag = document.createElement('a');
    anchorTag.href = fileURL;
    anchorTag.target = '_blank';
    anchorTag.download = 'CouponOrder.pdf';
    document.body.appendChild(anchorTag);
    anchorTag.click();
    document.body.removeChild(anchorTag);

1
这会下载文件。但是当尝试打开时,会显示为损坏的文件。 - San
@San,请在记事本中打开生成的文件,查看是否存在非PDF文档内容,例如从调用MyBusinessObject.GenerateMyReport中得到的500错误或异常消息。 - Brett Caswell
没有任何异常。我已经验证了 MyBusinessObject.GenerateMyReport 返回的数据大小,并直接将其写入到 PDF 文件中。 - San

2

一个简单的方法是使用GET方法在您的服务器上,并通过查询参数接收数据。

然后在您的客户端应用程序中,您只需要创建一个经典链接:

<a href="http://your-server.com/your-resource?param1=123&param2=456">Test Link</a>

如果你想从脚本逻辑中管理它,请使用这个简单的js脚本:

window.open("http://your-server.com/your-resource?param1=123&param2=456");

1
创建内容描述并将其添加到您的响应头中。
var cd = new System.Net.Mime.ContentDisposition
{
    // for example foo.bak
    FileName = System.IO.Path.GetFileName(fileName),
    // always prompt the user for downloading, set to true if you want 
    // the browser to try to show the file inline
    Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());

0

你的问题在于GetMyDocument函数将会接收到PDF文件作为服务器响应,但目前你没有对该响应做任何处理。你需要在then回调中处理这个响应。从JavaScript保存文件并不是完全简单的,因此我建议使用外部库,比如filesaver.js来帮助你。

最终代码可能会像这样...

_getMyDocument(e) {
    GetMyDocument(this.props.mydata)
    .then((response)=> {
        const returnedFile = new Blob([response], { type: 'application/pdf'});
        saveAs(returnedFile);
    }).catch(error=> {

    });

尝试使用Filesaver.js,如何从响应中读取数据?new File()需要两个参数,并期望数据为数组。 - San
var file = new Blob([response], { type: 'application/pdf' }); FileSaver.saveAs(file); 这会生成一个空白文档。在传递给 Blob 之前,是否需要进行任何转换? - San
如果您使用 new Blob([response.data]) 呢? - Alex Young
@AlexYoung:嗯,我们不一定需要外部库。我们也可以用纯JS来实现。请看我的回答。 - ScrapCode
在Textpad中打开下载的文档会显示文本"[object Object]"。我已通过控制器将myDoc写入pdf文件来验证数据,它成功地写入了数据。如果我发送响应的方式正确,我认为问题在于我在javascript中读取响应的方式-> var file = new Blob([response], { type: 'application/pdf' }。 - San
显示剩余3条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接