我正在开发一个代码库,我需要允许用户下载已经驻留在 AWS S3 上的 PDF 文档。我已经实现了一个用于先前功能的下载问题。
对于此功能,我需要在用户完成文件下载后更新 UI(进度步进器)。我最初认为这会很简单:
- 用户点击下载
- API 调用是在使用
send_data
下载文件的地方进行的。在此 API 调用中,我还将更新Foo
模型以更改状态以指示用户已下载文件; - 执行
redirect_to request.referer
重新加载数据。Foo
中的更改状态将负责在 UI 中显示更新进度;
我误以为这会很简单。复杂的原因:
send_data
已经在渲染数据,所以我无法使用redirect_to
刷新页面,因为这会触发多重渲染错误;send_data
不适用于remote: true
选项,因此通过 AJAX 链接请求数据和更新 ERB 模板已被淘汰;- 我可以将所有内容写入 JS
on click
函数,但这似乎有点 hack。我可能需要直接从 AWS 检索文件并跳过我的 api?我怀疑我可能会遇到 CORS 问题,因为我无法控制服务器。
这是我的 rails 下载方法目前的样子:
def download
attachment = Attachment.find_by_id(params[:attachment_id])
content = send_data(
attachment.file.read,
filename: "#{attachment.title}.#{attachment.file.file.extension}",
type: attachment.content_type,
disposition: "attachment",
)
end
基本上工作的 js 代码看起来像这样,其中所有相关路径和文件名都通过数据属性传递给 JS:
$(document).on("click", "#download", function(e){
e.preventDefault();
const data = $('#temp-information').data();
var req = new XMLHttpRequest();
req.open("GET", data.path, true);
req.responseType = "blob";
const filename = data.title;
req.onload = function (event) {
var blob = req.response;
console.log(blob.size);
var link=document.createElement('a');
link.href=window.URL.createObjectURL(blob);
link.download= filename;
document.body.appendChild(link);
link.click();
};
if (typeof window.navigator.msSaveBlob !== 'undefined') {
// Fix to work in IE11
window.navigator.msSaveBlob(blob, filename);
} else {
req.send();
}
});
在下载完成后处理文件下载和更新 UI 的最有效和 Rails 方式是什么?
最佳答案
您并非 100% 清楚您要完成的任务。如果您试图让用户看到下载进度,我不确定您是否真的需要执行除 send_data
之外的任何操作,然后大多数浏览器将开始下载文件,包括显示进度条.
因为您似乎想在文件下载完成后做一些事情,所以这有点棘手。该问题与 Rails 无关,您使用的方法在我看来非常合理。
关于 this SO thread您会发现对这个问题的冗长讨论以及人们尝试解决它的各种方法。通常,解决方案遵循相同的基本结构,即简单地轮询服务器。
在您的 Rails 应用程序中,您可以大致按如下方式实现。假设您在附件模型中添加了一个字段 status
...
def download
attachment = Attachment.find_by_id(params[:attachment_id])
attachment.update(status: "downloading")
send_data(
attachment.file.read,
filename: "#{attachment.title}.#{attachment.file.file.extension}",
type: attachment.content_type,
disposition: "attachment",
)
attachment.update(status: "complete")
end
然后您可以添加一个返回文件状态的端点。因此,当用户开始下载文件时,您就开始轮询该端点。
def attachment_status
attachment = Attachment.find_by_id(params[:attachment_id])
respond_to do |format|
format.json do
{status: attachment.status}
end
end
end
然后在 Javascript 中,例如使用 HttpPromise :
var http = new HttpPromise;
function poll(doneFn) {
http.get("/status.json") // you will need to set your actual status endpoint path here
.success(function(data,xhr){
if (data.status == "complete") {
doneFn();
}
});
};
function downloadFinished(){
// ... do whatever you want on finish here ...
};
setInterval(function(){ poll(downloadFinished) }, 5000);
这不是世界上最美好的事情,但它应该完成工作。
祝你好运!
关于javascript - 使用带有后续操作的 send_data 下载 Rails 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50252859/