【问题标题】:Vue + Laravel: How to properly download a PDF file?Vue + Laravel:如何正确下载 PDF 文件?
【发布时间】:2018-11-17 02:22:39
【问题描述】:

情况:

前端:Vue。后端:Laravel。

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

  • 我需要 Laravel 获取文件并将其作为 API GET 请求的响应返回。
  • 然后在我的 Vue Web 应用程序中,我需要获取文件并下载它。

代码:

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是空白的。

不知何故,数据被损坏了(不是这个特定的 pdf 文件的问题,我尝试了几个 pdf 文件 - 结果相同)

来自服务器的响应:

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

PDF:

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

问题:

如何使用 Laravel for API 和 Vue for web app 正确下载 pdf 文件?

谢谢!

【问题讨论】:

    标签: javascript php laravel pdf vue.js


    【解决方案1】:

    解决方案:

    上面的代码是正确的。缺少的是添加正确的responseTypearraybuffer

    我被回复中的???? 吓到了,这误导了我。 这些问号没问题,因为 pdf 是二进制数据,应该由适当的读者阅读。

    阵列缓冲区:

    而arraybuffer正是用来保存二进制数据的。

    这是来自Mozilla网站的定义:

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

    ResponseType 字符串表示响应的类型。通过告诉它一个数组缓冲区,它会相应地处理数据。

    只需添加 responseType,我就可以正确下载 pdf 文件。

    代码:

    这是更正后的 Vue 代码(与之前完全相同,但添加了 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()
        })
    }
    

    编辑:

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

    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)
    },
    

    【讨论】:

    • 非常感谢@FrancescoMussi!这很有帮助。我的 pdf 下载但无法打开或打开时出现无法打开的错误。当我添加 responseType 时,一切正常!
    • responseType 作为数组缓冲区!作品
    • 我已经在流式传输 PDF 数据上工作了两天了,我正在拔头发。我不断收到“未定义”作为我的 PDF 数据。我可以在网络中看到该死的 PDF 数据,但是我无法保存它!
    【解决方案2】:

    它对我有用。

    来自 laravel 后端:

    $pdf = PDF::loadView('your_view_name', ['data' => $data]);
    return $pdf->output();
    

    来自 vuejs 前端:

    axios({
    url: 'http://localhost:8000/api/your-route',
    method: 'GET',
    responseType: 'blob',
    }).then((response) => {
         var fileURL = window.URL.createObjectURL(new Blob([response.data]));
         var fileLink = document.createElement('a');
         fileLink.href = fileURL;
         fileLink.setAttribute('download', 'file.pdf');
         document.body.appendChild(fileLink);
         fileLink.click();
    
    });
    

    【讨论】:

      【解决方案3】:
      downloadFile: function () {
                  this.$http.post('{{ route('download.download') }}', {
                      _token: "{{ csrf_token() }}",
                      inputs: this.inputs
                  },{responseType: 'arraybuffer'}).then(response => {
                      var filename = response.headers.get('content-disposition').split('=')[1].replace(/^\"+|\"+$/g, '')
                      var url = window.URL.createObjectURL(new Blob([response.body],{type:response.headers.get('content-type')}))
                      var link = document.createElement('a')
                      link.href = url
                      link.setAttribute('download', filename)
                      document.body.appendChild(link)
                      link.click()
                  });
              },
      

      【讨论】:

      • 在您的答案中添加一些讨论,以帮助解释它如何解决 OP 的问题
      • 适用于 Vue 2.5.22 和 Vue-resource 1.5.1
      【解决方案4】:

      您将无法从Laravel 下载到Vue,因为我假设两者都在不同的端口上运行。

      即使你尝试这样的事情。

      public function getDownload()
          {
              //PDF file is stored under project/public/download/info.pdf
              $file= public_path(). "/download/info.pdf";
      
              $headers = [
                    'Content-Type' => 'application/pdf',
                 ];
      
          return response()->download($file, 'filename.pdf', $headers);
          }
      

      当您将标头发送到 Laravel 端口时,这无济于事 尝试使用 Vue js 库并尝试在库中发送该 pdf 内容

      试试这个 Get help from here

      【讨论】:

      • 看起来很相似。我将尝试使用 Axios 而不是 vue-resource。
      • 一定要让我知道它是否有效@johnnyfittizio
      • 在您提供的链接中,我找到了实际的解决方案。一切都是正确的,除了缺少 responseType 的事实。 Axios 或 vue-resource 无关紧要。我在答案中写了更正的代码。我相信你指出了正确的方向。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2018-12-13
      • 2021-02-17
      • 1970-01-01
      • 1970-01-01
      • 2013-12-03
      • 2019-08-25
      • 2011-05-26
      相关资源
      最近更新 更多