Vue + Laravel:如何正确下载PDF文件?

Vue + Laravel:如何正确下载PDF文件?

问题描述:

情况:

前端:Vue.后端:Laravel.

Frontend: Vue. Backend: Laravel.

在网络应用程序中,我需要让用户下载某些pdf文件:

Inside the web app I need to let the user download certain pdf files:

  • 我需要Laravel提取文件并作为API GET请求的响应返回.
  • 然后在我的Vue网络应用程序中,我需要获取文件并下载.

代码:

API:

$file = public_path() . "/path/test.pdf";

$headers = [
    'Content-Type' => 'application/pdf',
];
return response()->download($file, 'test.pdf', $headers);

网络应用:

downloadFile() {
  this.$http.get(this.apiPath + '/download_pdf')
    .then(response => {
      let blob = new Blob([response.data], { type: 'application/pdf' })
      let link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'test.pdf'
      link.click()
    })
}

结果:

使用此代码,我确实设法下载了pdf文件.问题是pdf是空白的.

Using this code I do manage to download a pdf file. The problem is that the pdf is blank.

以某种方式破坏数据(不是这个特定pdf文件的问题,我尝试了多个pdf文件-结果相同)

Somehow the data got corrupted (not a problem of this particular pdf file, I have tried with several pdf files - same outcome)

来自服务器的响应:

来自服务器的响应本身很好:

The response itself from the server is fine:

PDF:

问题可能出在pdf文件上.它肯定看起来已损坏的数据.这是response.data的摘要:

The problem may be with the pdf file. It definitely looks corrupted data. This is an excerpt of how it looks like the response.data:

问题:

如何使用Laravel的API和Vue的Web应用程序正确下载pdf文件?

How can I properly download a pdf file using Laravel for the API and Vue for the web app?

谢谢!

解决方案:

应归功于@Sagar,以向我指出正确的方向.

Due credits goes to @Sagar to point me in the right direction.

上面的代码是正确的.缺少的是将适当的responseType添加为arraybuffer.

The code above was correct. What was missing was adding the proper responseType as arraybuffer.

我对回答中的????感到害怕,这误导了我. 这些问号没问题,因为pdf是二进制数据,并且应由适当的读者阅读.

I got scared by those ???? inside the response, and that was misleading me. Those question marks were just okay since pdf is a binary data and is meant to be read by a proper reader.

ARRAYBUFFER:

而arraybuffer恰好用于保留二进制数据.

And arraybuffer is precisely used to keep binary data.

这是来自mozilla网站的定义:

This is the definition from the mozilla website:

ArrayBuffer对象用于表示通用的固定长度 原始二进制数据缓冲区.您不能直接操纵 ArrayBuffer;而是创建一个类型化数组对象之一,或者 一个DataView对象,它以特定格式表示缓冲区, 并使用它来读取和写入缓冲区的内容.

The ArrayBuffer object is used to represent a generic, fixed-length raw binary data buffer. You cannot directly manipulate the contents of an ArrayBuffer; instead, you create one of the typed array objects or a DataView object which represents the buffer in a specific format, and use that to read and write the contents of the buffer.

ResponseType字符串表示响应的类型.通过告诉它一个arraybuffer,然后它会相应地处理数据.

And the ResponseType string indicates the type of the response. By telling its an arraybuffer, it then treats the data accordingly.

通过添加responseType,我设法正确下载了pdf文件.

And just by adding the responseType I managed to properly download the pdf file.

代码:

这是经过更正的Vue代码(与以前完全一样,但是添加了responseType):

This is corrected Vue code (exactly as before, but with the addition of the responseType):

downloadFile() {
  this.$http.get(this.appApiPath + '/testpdf', {responseType: 'arraybuffer'})
    .then(response => {
      let blob = new Blob([response.data], { type: 'application/pdf' })
      let link = document.createElement('a')
      link.href = window.URL.createObjectURL(blob)
      link.download = 'test.pdf'
      link.click()
    })
}

这是一个更完整的解决方案,其中考虑了其他浏览器的行为:

This is a more complete solution that take into account other browsers behavior:

downloadContract(booking) {
  this.$http.get(this.appApiPath + '/download_contract/' + booking.id, {responseType: 'arraybuffer'})
    .then(response => {
      this.downloadFile(response, 'customFilename')
    }, response => {
      console.warn('error from download_contract')
      console.log(response)
      // Manage errors
      }
    })
},

downloadFile(response, filename) {
  // It is necessary to create a new blob object with mime-type explicitly set
  // otherwise only Chrome works like it should
  var newBlob = new Blob([response.body], {type: 'application/pdf'})

  // IE doesn't allow using a blob object directly as link href
  // instead it is necessary to use msSaveOrOpenBlob
  if (window.navigator && window.navigator.msSaveOrOpenBlob) {
    window.navigator.msSaveOrOpenBlob(newBlob)
    return
  }

  // For other browsers:
  // Create a link pointing to the ObjectURL containing the blob.
  const data = window.URL.createObjectURL(newBlob)
  var link = document.createElement('a')
  link.href = data
  link.download = filename + '.pdf'
  link.click()
  setTimeout(function () {
    // For Firefox it is necessary to delay revoking the ObjectURL
    window.URL.revokeObjectURL(data)
  }, 100)
},