文件下载 JS控制同时下载多个文件

一. 后端

import org.springframework.http.MediaType;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.stream.Collectors;
import java.util.stream.Stream;

@Controller
@RequestMapping("/file")
public class FileDownLoadController {
	
	// 下载页面初始化
    @RequestMapping("/init")
    public ModelAndView init() {

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName("fileDownload");
        return modelAndView;
    }
	
	// 获取文件名称或者文件路径
    @GetMapping("/getFileName")
    @ResponseBody
    public String getFileName() {

        String fileName = Stream.of("1.txt", "2.txt", "3.txt")
                .collect(Collectors.joining(","));
        return fileName;
    }
	
	// 下载文件
    @GetMapping("/manyFileDownLoad/{fileName}")
    public void manyFileDownLoad(@PathVariable("fileName") String fileName, HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 保存在本地磁盘中的文件
        File file = new File("C:\\Users\\xxx\\Desktop\\downloadFile\\" + fileName);
        FileInputStream fileInputStream = null;
        try {
            /*
                我们将文件以流的方式返回前端,但是流无法保存文件名称,因此我们将问文件名称放响应头中返回到前端,前端就可以获取到下载的文件名称了
                在响应头中设置文件名,通过URLEncoder.encode()进行文件编码防止文件名乱码
            */
            response.setHeader("Content-disposition", "attachment;filename=" + URLEncoder.encode(file.getName(), "UTF-8"));
            // 设置响应类型("application/octet-stream"),表明响应的内容的类型为二进制流
            response.setContentType(MediaType.APPLICATION_OCTET_STREAM_VALUE);

            // 读取本地的文件
            fileInputStream = new FileInputStream(file);

            // 根据response对象构造一个输出流对象,然后将输入流的对象写入输出流
            OutputStream os = response.getOutputStream();
            // 😁直接将文件一口气写入输出流中(文件不大的话,直接写入就行),Java9的语法
            os.write(fileInputStream.readAllBytes());

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                fileInputStream.close();
            }
        }
    }
}

二. 前端

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>文件下载</title>
</head>
<body>
    <button id="btn">点击同时下载多个文件</button>
</body>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js" referrerpolicy="no-referrer"></script>
<script th:inline="javascript">

    // 耗时函数
    const waitTime = async (milliseconds) => await new Promise((resolve) => setTimeout(resolve, milliseconds));

    // 上下文对象
    const ctxPath = [[${#httpServletRequest.getContextPath()}]];
    const url = `${ctxPath}/file`;

    $("#btn").click(async () => {

        // 获取要下载的文件名称
        const response = await fetch(url + '/getFileName');
        const result = await response.text();
        const fileNameList = result.split(",");

        // 把要从后端下载的文件保存到list中
        const fileBlobList = [];
        for (const fileName of fileNameList.values()) {

            // 向后端接口发送请求,获取响应对象
            const response = await fetch(`${url}/manyFileDownLoad/${fileName}`);
            // 😑将响应对象克隆一份
            const cloneRes = response.clone();
            // 😑获取文件流对象
            const file = await response.blob();
            // 😑将后端返回的文件流对象放入Blob对象中
            fileBlobList.push(new Blob([file]));

            // 🥳从响应头中可以获取出后端放入的文件名
            for (const [key, value] of cloneRes.headers) {
                if (key !== "content-disposition") {
                    continue;
                }
                console.log(value.split(";")[1])
            }
        }

        // 🥳创建下载文件用的隐藏a标签
        const aElement = document.createElement('a');
        aElement.style.display = 'none';
        document.body.appendChild(aElement);

        // 🥳遍历文件Blob对象,下载文件
        for (const [index, fileBlob] of fileBlobList.entries()) {

            // 🥳通过文件对象创建临时文件下载链接
            const src = URL.createObjectURL(fileBlob);
            aElement.href = src;
            aElement.download = fileNameList[index];
            aElement.click();
            // 移除内存中的临时文件对象
            URL.revokeObjectURL(src);

            // 🥳耗时行为,防止同一时间下载多个文件,触发浏览器的确认弹窗(根据情况添加,非必须)
            await waitTime(800);
        }

        // 🥳文件下载之后,移除我们定义的隐藏a标签
        document.body.removeChild(aElement);
    })
</script>
</html>

三. 效果

在这里插入图片描述

可以使用多线程或者异步加载的方式实现同时下载多个文件。 一种常见的实现方式是使用XMLHttpRequest对象进行异步加载,可以通过创建多个XMLHttpRequest对象来同时下载多个文件。例如: ```javascript function downloadFiles(urls, callback) { var downloaded = 0; var files = []; for (var i = 0; i < urls.length; i++) { var xhr = new XMLHttpRequest(); xhr.open('GET', urls[i], true); xhr.responseType = 'blob'; xhr.onload = function() { downloaded++; files.push(xhr.response); if (downloaded == urls.length) { callback(files); } }; xhr.send(); } } downloadFiles(['file1.txt', 'file2.txt', 'file3.txt'], function(files) { // 处理下载完成的文件 }); ``` 另一种实现方式是使用Web Worker进行多线程下载,可以在每个Worker中下载一个文件,从而实现同时下载多个文件。例如: ```javascript function downloadFiles(urls, callback) { var downloaded = 0; var files = []; var workers = []; for (var i = 0; i < urls.length; i++) { var worker = new Worker('download-worker.js'); worker.postMessage(urls[i]); worker.onmessage = function(event) { downloaded++; files.push(event.data); if (downloaded == urls.length) { callback(files); } }; workers.push(worker); } } downloadFiles(['file1.txt', 'file2.txt', 'file3.txt'], function(files) { // 处理下载完成的文件 }); ``` 其中,download-worker.js是一个独立的Worker脚本,用于下载单个文件并将下载完成的文件数据发送给主线程。例如: ```javascript self.onmessage = function(event) { var xhr = new XMLHttpRequest(); xhr.open('GET', event.data, true); xhr.responseType = 'blob'; xhr.onload = function() { self.postMessage(xhr.response); }; xhr.send(); }; ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值