使用 JS 消费 Rails send_data 响应

Posted

技术标签:

【中文标题】使用 JS 消费 Rails send_data 响应【英文标题】:Using JS to consume a Rails send_data response 【发布时间】:2019-07-16 07:22:49 【问题描述】:

我有一个连接到 Rails API 后端的 VueJS 前端。

在其中一个端点中,我使用WickedPDF 来生成 PDF。当我在浏览器中打开 URL 本身时,PDF 下载正常并且完全按预期工作。当我通过 Vue 发送请求时,API 会返回一个看起来很奇怪的字符串,如下所示:

%PDF-1.4
1 0 obj
<<
/Title (��)
/Creator (��wkhtmltopdf 0.12.4)
/Producer (��Qt 4.8.7)
/CreationDate (D:20190222102025+02'00')
>>
endobj
3 0 obj
<<
/Type /ExtGState
/SA true
/SM 0.02
/ca 1.0
/CA 1.0
/AIS false
...

我不确定这是什么数据类型?我最初认为它可能是一个 BLOB,但我不知道。我按照here 概述的逻辑来解析来自我的rails api 的响应,它确实将PDF 下载到chrome。打开此 PDF 时,它是空白的,并且 chrome 浏览器顶部的文件名是奇怪字符的组合。这让我觉得我没有以正确的方式转换响应,最终会发生一些编码问题。

这是我的 Rails API 代码:

def pdf
  pdf_html = ActionController::Base.new.render_to_string(
    template: 'api/v1/exporters/pdf',
    layout: 'pdf',
    page_size: 'A4',
    formats: :html,
    encoding: 'utf8',
    margin: 
      top: 20,
      left: 20,
    
  )
  pdf = WickedPdf.new.pdf_from_string(pdf_html)
  send_data(
    pdf,                                  
    filename: 'download.pdf',                     
    type: 'application/pdf',                      
    disposition: 'attachment'
  )
end

这是上面链接中的 JS 函数。我将 Rails 响应主体(当控制台记录为第一个代码块中设置的奇怪字符)作为唯一参数传递:

showFile(blob)
  var newBlob = new Blob([blob], type: "application/pdf")

  if (window.navigator && window.navigator.msSaveOrOpenBlob) 
    window.navigator.msSaveOrOpenBlob(newBlob);
    return;
   

  const data = window.URL.createObjectURL(newBlob);
  var link = document.createElement('a');
  link.href = data;
  link.download="file.pdf";
  link.click();
  setTimeout(function()
    window.URL.revokeObjectURL(data);
  , 100

谁能帮助我指出正确的方向,如何以正确的方式进行配置,或者如何以不同/更好的方式进行配置?甚至确认响应是哪种数据类型也可能对我有所帮助。

【问题讨论】:

你试过用window.location.assign(url)打开吗? 感谢@dan-klasson 我尝试将数据字符串作为 url 打开,但它只会导致空白屏幕 不,我的意思是使用window.location.assign(url),而url 是实际的url。您似乎没有传递任何标头数据,所以这不只是打开 url 并让浏览器完成其余工作的问题吗? 【参考方案1】:

如果您使用axios 进行API 调用,您可以指定客户端将数据下载为blob。

import axios from 'axios';

axios
    .request(
       url: '/api-url-to-download-pdf',
       responseType: 'blob',
    )
    .then(response => response.data)
    .then(blob => 
        const data = URL.createObjectURL(blob );

        // ... do your stuff here ...
    .catch((err) => 
        // handle error
    );

如果您使用fetch api 发出 API 请求,

fetch('/api-url-to-download-pdf', 
    headers: 
        Accept: 'application/pdf',
    ,
    responseType: 'arraybuffer'
)
    .then(response => 
        console.log(response);
        if (response.ok) 
            return response.blob();
        
    )
    .then(blob => 
        const data = URL.createObjectURL(blob);

        // ... do your stuff here ...
    )
    .catch((err) => 
        // handle error
    );

【讨论】:

URL 是“窗口”对象的一部分,因此 createObjectURL 所在的行应该类似于 const data = window.URL.createObjectURL(blob); 是的,正确的@AskarHussain 但是,由于window 的所有属性都是全局可访问的,我们真的不需要应用它。示例代码也应该可以正常工作。 const data = URL.createObjectURL(blob); 为我抛出以下错误TypeError: Failed to execute 'createObjectURL' on 'URL': Overload resolution failed. 我通过将其更改为const data = URL.createObjectURL(response.data); 并删除.then(blob =&gt; ... 部分来解决它。 这个答案很接近。但是这个:***.com/a/44021663/6772055 对我有用。希望这对其他人有所帮助。

以上是关于使用 JS 消费 Rails send_data 响应的主要内容,如果未能解决你的问题,请参考以下文章

使用 #send_data 从数据库发送图像与 Rails 4.2 冲突

如何渲染 XML 模板,然后在 Ruby on Rails 3.2.8 中使用 SEND_DATA?

流式导轨(上传中)

在节点js中使用套接字连接事件

Ember.js + Leaflet + rails(使用 Javascript MVC 创建 Rails 应用程序并打开源地图)

在 JS 模块中使用 Rails-UJS(带有 webpacker 的 Rails 6)