当前位置: 首页 > news >正文

JavaScript下载文件(简单模式、跨域问题、文件压缩)

文章目录

  • 简介
  • 简单文件下载
    • 通过模拟form表单提交
    • 通过XMLHttpRequest方式
  • 跨域(oss)下载并压缩文件
    • 完整示例
    • 文件压缩
    • 跨域设置

简介

相信各位开发朋友都遇到过下载的文件的需求,有的非常简单,基本链接的形式就可以。

有的就比较复杂,涉及跨域和压缩文件,例如,文件在OSS中,有的oss不支持压缩文件,要下10个文件就得弹10个下载出来。

业务老师多半是没有办法接受这种情况,怎么处理呢?

这就涉及到跨域获取文件并压缩文件了。

本文会介绍一下简单下载和下载OSS文件并压缩。

简单文件下载

首先我们看一些2种简单下载方式

通过模拟form表单提交

function downloadRemoteFile(url,materialId) {var body = document.getElementsByTagName('body')[0];var form = document.createElement('form');form.method = 'POST';form.action = url;var param = document.createElement('input');param.type = "hidden";param.name = "materialId";param.value = materialId;form.appendChild(param);body.appendChild(form);form.submit();body.removeChild(form);
}

上面这种方式:

  1. 优点是简单
  2. 缺点是错误不友好,出错了,没有提示信息

如果希望错误信息友好一点,可以通过XMLHttpRequest方式。

通过XMLHttpRequest方式

function downloadRemoteFileXMLHttpRequest(url,materialId) {console.log("${downloadUrl}" + " " + materialId);var xhr = new XMLHttpRequest();xhr.open("POST", url, true);xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded")xhr.onload = function () {if (xhr.status === 200) {if (xhr.response == null || xhr.response == "" || xhr.response == undefined) {alert("下载文件出错,请检查文件是否存在:" + materialId);return;}// 获取判断Content-Type// var contentType = xhr.getResponseHeader("Content-Type");var blob = new Blob([xhr.response], { type: "application/octet-stream" });var fileName = getFileNameFromResponseHeader(xhr);var link = document.createElement("a");link.href = window.URL.createObjectURL(blob);link.download = fileName;link.click();link.remove();window.URL.revokeObjectURL(link.href);} else {alert("下载响应异常");console.log(xhr);}};xhr.send("materialId=" + materialId);
}function getFileNameFromResponseHeader(xhr) {var contentDisposition = xhr.getResponseHeader("content-disposition")var matchResult = /filename[^;=\n]*=((['"]).*?\2|[^;\n]*)/.exec(contentDisposition);if (matchResult != null && matchResult[1]) {return decodeURIComponent(matchResult[1].replace(/['"]/g, ""));}return "default-name";
}

通过XMLHttpRequest方式就灵活很多,虽然还是模拟了a链接,但是能先处理响应。

例如,下载后端可以能有一些预检查,如果预检查都没有通过,那么可能返回的就不是Blob文件,而是一个json。

通过XMLHttpRequest方式就可以通过判断ContentType内容,来获取文件流、或是json的内容,从而把错误信息比较友好的展示给用户。

对于后端下载接口,可能更好的方式是把信息写到header中,这样对于前端来说就能更好统一处理逻辑。

response.addHeader("success", "false");
response.addHeader("message", URLEncoder.encode("该数据您无权下载", StandardCharsets.UTF_8));

后端不同处理方式(仅仅演示,实际操作最后统一逻辑):

@RequestMapping("/download")
public void download(HttpServletResponse response, @RequestParam("fid") Long fid) throws IOException {if (fid < 0) {response.setContentType("application/json");PrintWriter writer = response.getWriter();Result<Void> result = ResultHelper.getFailResult("文件不存在");writer.write(JSON.toJSONString(result));return;}if (fid < 100) {response.addHeader("success", "false");String message = "该数据您无权下载";response.addHeader("message", URLEncoder.encode(message, StandardCharsets.UTF_8));return;}try (FileInputStream fis = new FileInputStream("F:\\tmp\\季报统计表.xlsx");OutputStream os = response.getOutputStream()) {String fileName = URLEncoder.encode("季报统计表.xlsx",StandardCharsets.UTF_8);response.reset();response.setContentType("application/octet-stream");response.setHeader("Content-Disposition", "attachment;filename=" + fileName);response.addHeader("success", "true");IOUtils.copy(fis, os);}
}

跨域(oss)下载并压缩文件

下载并压缩文件主要有2个不好处理的问题:

  1. 跨域
  2. 文件流压缩

解决方案:

  1. 跨域问题主要是浏览器限制,可以通过后端添加header Access-Control-Allow-Origin或者直接设置浏览器处理
  2. 文件压缩可以使用jszip

完整示例

<!DOCTYPE html>
<html><head><meta charset="utf-8"><title>跨域下载</title></head><body><button onclick="download()">下载</button></body>
</html>
<script type="text/javascript" src="js/jszip.js"></script>
<script>function fetchFile(url, zip) {window.fetch(url, {method: "GET",// mode: 'no-cors',mode: 'cors' // 允许跨越}).then(response => {// no-cors 这里不执行return response.blob();}).catch(error => {console.log(error);});}// 下载文件function downloadFile(url, fileName) {const a = document.createElement('a')a.style.display = 'none'a.href = urla.download = fileNamedocument.body.appendChild(a)a.click()document.body.removeChild(a)}function download() {const zip = new JSZip();// 从远程服务器获取文件const urls = ['http://127.0.0.1:8087/file/download2'];let map = new Map();let promises = [];var index = 0;urls.forEach(function(value, index, array) {// cors 表示可以跨越,服务端必须设置Access-Control-Allow-Origin//no-cors fetch不会执行then,拿不到文件var result = fetch(value, {mode: 'cors'}).then(response => response.blob());promises.push(result);map.set(index++, value);});Promise.all(promises).then(results => {var flag = true;if (results.every(result => result !== undefined)) {flag = true;} else {flag = flase;}if(flag){index = 0;results.forEach(blob => {// promise 结果是按顺序返回的var url = map.get(index++);var filename = getFileName(url);zip.file(filename, blob, {binary: true});});zip.generateAsync({type: "blob"}).then(function(content) {const url = window.URL.createObjectURL(content);downloadFile(url, "result.zip");});}else{alert("有文件下载失败请重试!");}}).catch(error => console.error('Error:', error));}function getFileName(url) {var url = url.substring(url.lastIndexOf("/") + 1);var idx = url.lastIndexOf("?");if (idx > 0) {url = url.substring(0, idx);}return decodeURI(url);}
</script>

文件压缩

首先下载jszip的最新版本,注意使用新版本(3.10.x)和老版本的api差别较大。

jszip下载

function zip() {const zip = new JSZip();// 添加一个文本文件zip.file("Hello.txt", "Hello World\n");// 添加一个json文件let jsonData = {};const blob = new Blob([JSON.stringify(jsonData, null, 4)], {type: 'application/json'});zip.file('notes.json', blob);// 添加文件夹const folder = zip.folder("subdir");// 在文本文件中添加一个文本文件folder.file("Hi.txt", "Hi\n");// 除了blob类型,还可以设置为base64zip.generateAsync({type: "blob"}).then(function(content) {const url = window.URL.createObjectURL(content);downloadFile(url, "result.zip");});
}// 下载文件
function downloadFile(url, fileName) {const a = document.createElement('a')a.style.display = 'none'a.href = urla.download = fileNamedocument.body.appendChild(a)a.click()document.body.removeChild(a)
}

跨域设置

如果你看到一个类似于下面的错误,那是因为浏览器限制,不允许跨域。

跨域错误

Access to fetch at ‘http://127.0.0.1:8087/file/download2’ from origin ‘http://127.0.0.1:8848’ has been blocked by CORS policy: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

解决这个问题的方法有2个:

  1. 服务端返回response的header Access-Control-Allow-Origin值类似于*
  2. 设置浏览器

设置服务器的方式很多,以springboot为例:

可以通过设置Filter:

import jakarta.servlet.Filter;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;import java.io.IOException;@Component
public class CorsFilter implements Filter {@Overridepublic void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {HttpServletResponse res = (HttpServletResponse) response;res.setHeader("Access-Control-Allow-Origin", "*");res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
//        res.setHeader("Access-Control-Allow-Headers", "Authorization");res.setHeader("Access-Control-Allow-Headers", "*");chain.doFilter(request, response);}
}

可以设置WebMvcConfigurer:

package vip.meet.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;@Configuration
public class CorsConfig implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOrigins("*").allowedMethods("GET", "POST", "PUT", "DELETE").allowCredentials(true).allowedHeaders("*");}
}

可以直接在单个请求中设置:response.setHeader(“Access-Control-Allow-Origin”, “*”);

OSS中设置:

oss跨域设置

设置这些的目的只有一个就是然response中有Access-Control-Allow-Origin

response Access-Control-Allow-Origin

除了设置服务端,还可以设置浏览器,以Chrome为例:

浏览器跨域设置

设置浏览器的启动参数:

–disable-web-security --user-data-dir=D:\ChromeDevData

相关文章:

JavaScript下载文件(简单模式、跨域问题、文件压缩)

文章目录 简介简单文件下载通过模拟form表单提交通过XMLHttpRequest方式 跨域(oss)下载并压缩文件完整示例文件压缩跨域设置 简介 相信各位开发朋友都遇到过下载的文件的需求&#xff0c;有的非常简单&#xff0c;基本链接的形式就可以。 有的就比较复杂&#xff0c;涉及跨域…...

Django 定义使用模型,并添加数据

教材&#xff1a; Python web企业级项目开发教程&#xff08;黑马程序员&#xff09;第三章 模型 实验步骤&#xff1a; 1.创建项目和应用 前置步骤可看前文&#xff0c;进入到指定文件位置后创建 django-admin startproject mysite python manage.py startapp app01 2.注册…...

联名物料常泄漏?一端叠满“安全buff”

前段时间&#xff0c;一则关于爆火影视剧与知名茶饮品牌联名的消息在社交平台上迅速传播&#xff0c;宣传物料的照片也随之曝光——门店尚未上新&#xff0c;“小道消息”便已被疯传。但这种情况并非首次发生&#xff0c;让众多网友不禁猜想&#xff1a;这究竟是一场精心策划的…...

Flutter UI组件库(JUI)

Flutter UI组件库 (JUI) 介绍 您是否正在寻找一种方法来简化Flutter开发过程&#xff0c;并创建美观、一致的用户界面&#xff1f;您的搜索到此为止&#xff01;我们的Flutter UI组件库&#xff08;JUI&#xff09;提供了广泛的预构建、可自定义组件&#xff0c;帮助您快速构建…...

国外电商系统开发-运维系统远程文件

设计初衷是为了让所有人都能方便的打开网页&#xff0c;就能查看Linux系统文件内容&#xff0c;而不再用cat、vim、more等命令去打开文件&#xff0c;这对于我们一个普通的研发或者是财务人员来说&#xff0c;显得太繁琐&#xff0c;因为他们很可能不会这些命令&#xff0c;其次…...

4. Node.js Path模块

2.3Path模块 2.3.1获取js文件的绝对路径 console.log(__dirname) //js文件所在的文件夹的绝对路径 console.log(__filename) //js文件的绝对路径输出&#xff1a; G:\py_project\nodejs_study G:\py_project\nodejs_study\file.js2.3.2拼接规范的绝对路径path.r…...

重构长方法之分解条件表达式

分解条件表达式 是一种重构长方法中常用的技术&#xff0c;它适用于复杂的条件逻辑。在方法中&#xff0c;条件分支&#xff08;if-else 或 switch&#xff09;有时会变得条件非常多&#xff0c;非常复杂&#xff0c;难以理解和维护。通过分解条件逻辑&#xff0c;可以让代码更…...

蚁群算法养老服务人员智能调度系统

养老行业近年来越发热门&#xff0c;如何有效调配服务人员成为许多机构的痛点。我们结合智能算法技术&#xff0c;开发出了一款专为养老行业量身打造的“蚁群算法养老服务人员调度系统”&#xff0c;能够精准、高效地为机构分配人员&#xff0c;从此告别人力资源调度难题。 系…...

java使用 IDEA自动补全功能 AI 插件

国内插件&#xff1a; CodeGeeX&#xff1a; 功能特性&#xff1a; 由国内团队开发&#xff0c;是一款智能编程助手插件。它集成了多种人工智能技术&#xff0c;能够在多个编程语言中提供智能代码补全、代码生成、代码优化和注释生成等功能。该插件特别适用于常见的编程任务…...

【ShuQiHere】 AI与自我意识:能否创造真正的自觉机器人?

&#x1f916;【ShuQiHere】 &#x1f4dc; 目录 引言人类意识的探索机器意识的五大理论 功能主义&#xff08;Functionalism&#xff09;信息整合&#xff08;Information Integration&#xff09;体现主义&#xff08;Embodiment&#xff09;行动主义&#xff08;Enaction&…...

【Linux 从基础到进阶】CPU性能调优与监控

CPU性能调优与监控 1. 引言 在计算机系统中&#xff0c;CPU是核心组件之一&#xff0c;其性能直接影响系统的整体表现和响应速度。无论是在企业服务器环境、虚拟化环境&#xff0c;还是大数据计算场景&#xff0c;优化和监控CPU性能都至关重要。通过合理的调优策略和监控工具…...

Centos基线自动化检查脚本

此脚本是一个用于检查Linux系统安全配置的Bash脚本。它通过多项安全标准对系统进行评估&#xff0c;主要检查以下内容&#xff1a; IP地址获取&#xff1a;脚本首先获取主机的IP地址&#xff0c;确保其以10.115开头。 密码策略检查&#xff1a; 检查最小密码长度&#xff08;P…...

OpenCV答题卡识别

文章目录 一、基本流程二、代码实现1.定义函数2.图像预处理&#xff08;1&#xff09;高斯模糊、边缘检测&#xff08;2&#xff09;轮廓检测&#xff08;3&#xff09;透视变换&#xff08;4&#xff09;阈值处理和轮廓检测 3.筛选和排序选项轮廓4.判断答案5.显示结果 三、总结…...

通用数据库对象设计

1. 公共属性 这里的数据模型以陈品山的实体-关系模型为基础&#xff0c;增加了两点修改。一是用“组”的概念表达实体间关系&#xff0c;并将组作为一种特殊实体。二是采用继承的思想&#xff0c;将实体的公共属性提取出来&#xff0c;放到统一表中。实体的特有属性保存在单独…...

Java基础12-特殊文件和日志技术

十二、特殊文件和日志技术 1、特殊文件 properties&#xff1a;用来存储键值对数据。 xml&#xff1a;用来存储有关系的数据。 1.1 properties文件 特点&#xff1a;存储键值对&#xff0c;键不能重复&#xff0c;文件后缀一般是.properties结尾的。 properties&#xff1a;是…...

2.4 STM32启动过程

目录 一,启动Flow 1.1 初始化MSP 1.2 初始化PC 1.3 设置堆栈大小 1.4初始化中断向量表 1.5 调用初始化函数(可选) 1.6 调用__main 二,Reset_Handler函数 一,启动Flow 下面是stm32在内部FLASH启动的启动建议流程图,在stm32复位到执行我们程序的main函数的过程中,…...

rm: cannot remove: Device or resource busy 解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…...

2024年的5款AI写作工具,你用过几个?

AI技术正逐渐渗透到我们生活的方方面面&#xff0c;其中就包括写作领域。今天&#xff0c;我要为大家介绍5款实用的AI写作助手&#xff0c;它们将帮助你提高写作效率&#xff0c;激发创作灵感&#xff0c;让你的写作之路更加顺畅。 1. 宙语Cosmos-全能写作助手 网址&#xff…...

泛癌热门靶点TROP2及研究工具试剂

前 言 TROP2属于肿瘤相关抗原之一&#xff0c;在多种肿瘤中表达升高&#xff0c;促进肿瘤细胞生长、增殖和转移。TROP2已经成为近年来NEJM、肿瘤学期刊、药物研发的多重热点。通过PubMed检索到477篇相关文献&#xff0c;自2020年文献数量逐步增加&#xff0c;2022年达81篇&am…...

2848. 与车相交的点

2848. 与车相交的点 题目链接&#xff1a;2848. 与车相交的点 代码如下&#xff1a; class Solution { public:int numberOfPoints(vector<vector<int>>& nums){set<int> s;for (int i 0; i < nums.size(); i){for (int j nums[i][0]; j < nu…...

React hook之useRef

React useRef 详解 useRef 是 React 提供的一个 Hook&#xff0c;用于在函数组件中创建可变的引用对象。它在 React 开发中有多种重要用途&#xff0c;下面我将全面详细地介绍它的特性和用法。 基本概念 1. 创建 ref const refContainer useRef(initialValue);initialValu…...

逻辑回归:给不确定性划界的分类大师

想象你是一名医生。面对患者的检查报告&#xff08;肿瘤大小、血液指标&#xff09;&#xff0c;你需要做出一个**决定性判断**&#xff1a;恶性还是良性&#xff1f;这种“非黑即白”的抉择&#xff0c;正是**逻辑回归&#xff08;Logistic Regression&#xff09;** 的战场&a…...

iPhone密码忘记了办?iPhoneUnlocker,iPhone解锁工具Aiseesoft iPhone Unlocker 高级注册版​分享

平时用 iPhone 的时候&#xff0c;难免会碰到解锁的麻烦事。比如密码忘了、人脸识别 / 指纹识别突然不灵&#xff0c;或者买了二手 iPhone 却被原来的 iCloud 账号锁住&#xff0c;这时候就需要靠谱的解锁工具来帮忙了。Aiseesoft iPhone Unlocker 就是专门解决这些问题的软件&…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院挂号小程序

一、开发准备 ​​环境搭建​​&#xff1a; 安装DevEco Studio 3.0或更高版本配置HarmonyOS SDK申请开发者账号 ​​项目创建​​&#xff1a; File > New > Create Project > Application (选择"Empty Ability") 二、核心功能实现 1. 医院科室展示 /…...

基础测试工具使用经验

背景 vtune&#xff0c;perf, nsight system等基础测试工具&#xff0c;都是用过的&#xff0c;但是没有记录&#xff0c;都逐渐忘了。所以写这篇博客总结记录一下&#xff0c;只要以后发现新的用法&#xff0c;就记得来编辑补充一下 perf 比较基础的用法&#xff1a; 先改这…...

【算法训练营Day07】字符串part1

文章目录 反转字符串反转字符串II替换数字 反转字符串 题目链接&#xff1a;344. 反转字符串 双指针法&#xff0c;两个指针的元素直接调转即可 class Solution {public void reverseString(char[] s) {int head 0;int end s.length - 1;while(head < end) {char temp …...

从零开始打造 OpenSTLinux 6.6 Yocto 系统(基于STM32CubeMX)(九)

设备树移植 和uboot设备树修改的内容同步到kernel将设备树stm32mp157d-stm32mp157daa1-mx.dts复制到内核源码目录下 源码修改及编译 修改arch/arm/boot/dts/st/Makefile&#xff0c;新增设备树编译 stm32mp157f-ev1-m4-examples.dtb \stm32mp157d-stm32mp157daa1-mx.dtb修改…...

Fabric V2.5 通用溯源系统——增加图片上传与下载功能

fabric-trace项目在发布一年后,部署量已突破1000次,为支持更多场景,现新增支持图片信息上链,本文对图片上传、下载功能代码进行梳理,包含智能合约、后端、前端部分。 一、智能合约修改 为了增加图片信息上链溯源,需要对底层数据结构进行修改,在此对智能合约中的农产品数…...

腾讯云V3签名

想要接入腾讯云的Api&#xff0c;必然先按其文档计算出所要求的签名。 之前也调用过腾讯云的接口&#xff0c;但总是卡在签名这一步&#xff0c;最后放弃选择SDK&#xff0c;这次终于自己代码实现。 可能腾讯云翻新了接口文档&#xff0c;现在阅读起来&#xff0c;清晰了很多&…...

免费数学几何作图web平台

光锐软件免费数学工具&#xff0c;maths,数学制图&#xff0c;数学作图&#xff0c;几何作图&#xff0c;几何&#xff0c;AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...