问题描述
我目前正在 Web 应用程序中实现文件下载。 我在弹出阻止程序方面有点挣扎,下载需要在用户交互后立即开始,所以不能有服务器往返。 现在我注意到,例如,Goolge Drive 允许您下载文件夹。当您这样做时,他们的服务器首先会创建一个压缩的 zip 文件,这需要一些时间。服务器完成后,下载会立即开始,无需其他用户交互。
现在我想知道如何做到这一点?
解决方法
我写了一个函数来通过 url 下载文件。 在您的情况下,您必须使用 ajax 请求在服务器上制作一个 zip 文件,然后返回 url 并运行以下函数进行下载:
function download(url,filename){
fetch(url)
.then(resp => resp.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.style.display = 'none';
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
console.log('your file has downloaded!');
})
.catch(() => console.log('Download failed!'));
}
在这里测试代码笔:download file via url
,我注意到,例如 Google Drive 允许您下载 文件夹。当你这样做时,他们的服务器首先创建一个压缩的 zip 文件,这需要一些时间。
或者,您可以将目录的内容请求为 json,然后遍历 json 并将每个文件下载为 blob 并创建一个 zip 文件,唯一的阻塞是对 json 的请求,然后您可以显示下载的每个文件的状态等
库来做到这一点:
片段示例,使用 vue、s3 等
async download(bucket) {
this.$snackbar.show({
type: 'bg-success text-white',message: 'Building Zip,please wait...'
})
//..eek fetch all items in bucket,plop into a zip,then trigger download
// - there is some limits on final filesize,will work around it by disabling download :)
// resolve objects
const objects = async(bucket) => new Promise(async(resolve,reject) => {
let objectStream = await this.state.host.client.listObjectsV2(bucket,'',true)
let objects = []
//
objectStream.on('data',(obj) => {
if (obj && (obj.name || obj.prefix)) {
this.$snackbar.show({
type: 'bg-success text-white',message: 'Fetching: ' + obj.name
})
objects.push(obj)
}
})
objectStream.on('end',() => resolve(objects))
objectStream.on('error',(e) => reject(e))
})
// get an objects data
const getObject = (bucket,name) => new Promise((resolve,reject) => {
this.state.host.client.getObject(bucket,name,(err,dataStream) => {
let chunks = []
dataStream.on('data',chunk => {
this.$snackbar.show({
type: 'bg-success text-white',message: 'Downloading: ' + name
})
chunks.push(chunk)
})
dataStream.on('end',() => resolve(Buffer.concat(chunks || [])))
})
})
// fetch objects info a zip file
const makeZip = (bucket,objects) => new Promise(async(resolve,reject) => {
let zip = new JSZip()
for (let i in objects) {
this.$snackbar.show({
type: 'bg-success text-white',message: 'Zipping: ' + objects[i].name
})
zip.file(objects[i].name,await getObject(bucket,objects[i].name));
}
zip.generateAsync({
type: "blob"
}).then(content => {
this.$snackbar.show({
type: 'bg-success text-white',message: 'Zip Created'
})
resolve(content)
})
})
// using FileSaver,download file
const downloadZip = (content) => {
this.$snackbar.show({
type: 'bg-success text-white',message: `Downloading: ${bucket.name}.zip`
})
FileSaver.saveAs(content,`${bucket.name}.zip`)
}
try {
downloadZip(await makeZip(bucket.name,await objects(bucket.name)))
} catch (e) {
this.$snackbar.show({
type: 'bg-danger text-white',message: e.message
})
console.error(e)
}
},
如果你想要一个丑陋的方式来下载一个目录,获取json列表然后用document.createElement('a')
在dom上放置一堆a.setAttribute("target","_blank")
,但是你会得到一堆“另存为”对话框打开。