Web application development file download


In web application development, there are often places where users need to download files, usually in the following ways:

  • Front-end hyperlink downloads back-end static/entity files
  • Ajax method to download hyperlink backend static/entity files
  • Ajax method to obtain the files in the response package returned by the backend
  • The browser receives the corresponding package returned by the backend and directly downloads the files in it.

In addition to this, there are files that are rendered on the page rather than downloaded directly. For example, video streaming, image verification code, etc.

Hyperlink download file

Using hyperlinks to download files is the simplest and most common way to download files. The files can be static files or dynamically generated entity files.

The usage is mainly on the front end. The hyperlink url points to the address of the file, and the back end just needs to do the url routing.

Using this method, if the file you want to download can be opened directly by the browser, such as jpg and other pictures, it will be displayed in the browser instead of opening the download dialog box.

JS download hyperlink file (front-end)

You need to first obtain the hyperlink URL address where the file is located, then you can create a temporary hyperlink and trigger the click event of this temporary hyperlink.

// 创建临时超链接
let link = document.createElement('a');
// 给临时超链接添加 download 属性,设置保存文件名。
link.download = 'test.jpg';
// 给临时超链接添加 url
link.href = url;
// 触发 click 事件
link.click();

This method actually uses the new features of H5. If downloadthe attribute is not set, it will be the same as clicking a hyperlink, and the browser may directly open the supported file for viewing. In addition, this method will download the file directly to the default saving directory set by the browser.

It should be noted that this method is only suitable for images of the same origin. If it is cross-domain, you need to use canvas

Get the file in response (backend)

Files downloaded using hyperlinks are entity files, that is, the URL can find the file itself. If the file is created temporarily and the binary file is saved in the buffer, use this method. The backend uses python + django as an example. The actual main purpose is to operate the corresponding package, and any language or tool can be used.

# file 是文件对象,使用 file.getvalue() 能够获取其二进制字节
# 先创建响应对象
resp = HttpResponse()
# 将二进制字节写入响应对象的 content 中,也就是写入响应体(response 的 body)
# 也可以在创建响应包时写入 resp = HttpResponse(content=file.getvalue())
resp.content = file.getvalue()		# 或使用 resp.write(file.getvalue())
# 设置响应头的 content-type 为文件流
# 如果是图片或其他格式,则可以设置 content-type 为相应格式
# 例如 'image/png' 'application/pdf' 'image/jpeg' 'application/x-zip-compressed'
resp['Content-Type'] = 'application/octet-stream'
# 因为文件是从内存中的文件对象获取,没有文件名,所以在响应头中设置文件名
# 如果只是在浏览器缓存中使用,而不是下载,则可以不设置文件名
# 例如是验证码图片类随机生成的,通过 img 标签的 src 属性访问获取的
resp['Content-Disposition'] = 'attachment;filename=' + filename
# 响应包设置好了,将此响应包发给前端即可

When the front end receives the encapsulated response packet, it will start downloading the file.

It should be noted that if the file name is in Chinese, it needs to be urlencoded.

from urllib.parse import quote

resp['Content-Disposition'] = "attachment;filename*=UTF-8''" + quote(filename)

Note: UTF-8 is followed by two single quotes. If not added, it will be UTF-8considered as part of the file name. In addition, if it is written as "attachment;filename=" + quote(filename), without encoding, some browsers will cause encoding errors.

Backend transfers binary file objects

The file object transmitted by the backend, that is, the file object in the above example, can be an open file object or a cached file object. To open an entity file, use open()the method, and to cache the file object, use:

# 例如创建了 Excel 的 Workbook 对象 wb ,并已经完成写入数据
# 先创建缓存对象
buf = io.BytesIO()
# 将 wb 保存至缓存对象
wb.save(buf)
# 至此 buf 就可以当实体文件对象使用了,例如使用 file.getvalue() 能够获取其二进制字节

This also works:

buf = io.BytesIO(wb)

It mainly depends on whether the basic parent class inherited by wb is io.BytesIO()a parameter supported by the method. If it is not supported, you still need to use its own saving method to save it to the cache file object.

Also note: After using the file cache object, remember to release the resources

buf.close()

Native ajax gets files in response (front-end)

The file in response is in binary form, and ajax needs to obtain its content to create a Blob object. In addition, jQuery's ajax seems to be unable to process binary files correctly and will report an error: No conversion from text to arraybufferso native ajax is used here.

let xhr = new XMLHttpRequest();
xhr.open('POST', url, true);
// 如果需要附加数据,则需要增加数据类型
// xhr.setRequestHeader('Content-Type', 'application/json');
// 对象序列化作为数据发送
// let data = JSON.stringify({...});
xhr.responseType = 'arraybuffer';
// 回调函数
function success(resp, disposition){
    
    
	let blob = new Blob([resp], {
    
    type: 'application/octet-stream'});	// 获取到 blob 数据对象
	// 如果需要直接下载,则可以创建 a 标签
	let link = document.createElement('a');
	// 使用 blob 对象创建 url
	link.href = window.URL.createObjectURL(blob);
	// 根据响应头设置下载的文件名
	// disponsition 是发送时请求头中的 'Content-Disposition',如果进行了 urlencode 编码则需要注意解码
	// decodeURI(disposition.split("UTF-8''")[1]);
	link.download = disposition.split('=')[1];
	// 触发 click 事件,下载文件
	link.click();
	// 释放临时创建的 url
	window.URL.revokeObjectURL(link.href);
}
// xhr 的监听函数,当请求发送完成且接收到 200 的响应后,使用回调函数 success 来进行处理
xhr.onreadystatechange = function(){
    
    
	if (xhr.readyState === 4 && xhr.status === 200){
    
    
		return success(xhr.response, xhr.getResponseHeader('Content-Disposition));
	} else {
    
    
		return fail(xhr.status);	// 在失败回调函数中需判断 xhr.status,表示接受到响应。否则请求发送阶段也会执行回调函数
	}
}
xhr.send();
// 如附带数据,则使用 xhr.send(data);

This method is to hand over the response file to ajax for processing, using the blob object. The disadvantages of this are:

  • Different browsers have different support for blobs and have different restrictions.
  • You must wait until the file is downloaded before the blob object can be constructed and then converted into a file. If the downloaded file is relatively large, users will find that they cannot see the download prompt after clicking the download button.

Using ajax for downloading can perform some preprocessing:

  • Permission verification can restrict downloading based on user permissions, whether high-speed downloading is possible, etc.
// 不用 blob 来构建 URL,而是通过后端服务器来计算出用户的下载链接,然后再动态创建 <a> 标签的方式来实现下载
fetch('http://somehost/check-permission', options).then(res => {
    
    
    if (res.code === 0) {
    
    
        var a = document.createElement('a');
        var url = res.data.url;
        var filename = 'myfile.zip';
        a.href = url;
        a.download = filename;
        a.click();
    } else {
    
    
        alert('You have no permission to download the file!');
    }
});
  • Dynamic files, such as exporting data to Excel. At this time, because the corresponding URL does not exist, a request needs to be sent to the server to dynamically generate the data file.

Also, use js to get the response header

    function get_response(){
    
    
        let req = new XMLHttpRequest();
        req.open('GET',document.location,false);
        req.send(null);
        let header = req.getAllResponseHeaders().toLowerCase();
        console.log(header);
        console.log(req.getResponseHeader('content-type'));
    }

In addition, the front end uses the fetch method to obtain the file

try {
    
    
  const imgURL = 'https://dummyimage.com/300.png';
  const data = await fetch(imgURL);
  const blob = await data.blob();
  // 获取到 blob 对象,可以保存到文件,这里复制到剪切板
  await navigator.clipboard.write([		
    new ClipboardItem({
    
    
      [blob.type]: blob
    })
  ]);
  console.log('Image copied.');
} catch (err) {
    
    
  console.error(err.name, err.message);
}

Guess you like

Origin blog.csdn.net/runsong911/article/details/127656442