我正在开发代码库,我需要允许用户下载已驻留在AWS S3上的PDF文档。我已经实现了用于以前功能的下载问题。
对于此功能,我需要在用户完成文件下载后更新UI(进度步进器)。我最初认为这很简单:
send_data
进行API调用。在此API调用中,我还会更新Foo
模型以更改状态以指示用户已下载文件; redirect_to request.referer
重新加载数据。 Foo
中更改的状态将负责显示UI中的更新进度; 我错误地认为这很简单。复杂的原因:
send_data
已经在渲染数据,因此我无法使用redirect_to
刷新页面,因为这会触发多次渲染错误; send_data
不适用于remote: true
选项,因此通过AJAX链接请求数据并更新ERB模板; on click
函数,但这看起来有点像黑客。我可能需要直接从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:
$(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();
}
});
什么是最有效的& rails'y处理文件下载的方式&下载完成后更新UI?
答案 0 :(得分:1)
它并不是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);
它不是世界上最美丽的东西,但它应该完成工作。
祝你好运!