前言
本文总结Web应用开发中文件上传、下载的方法,即从前端表单输入文件并封装表单数据,然后请求后端服务器的处理过程;从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明,并给出了前端常用的axios库的请求方式,然后给出了后端Spring实现文件服务器的方法,其中就文件上传中遇到的大小限制、跨域请求、外链请求等关键点进行了说明
上传文件
前端上传请求发起的方式:若不依赖其他库常使用XMLHttpRequest、Fetch API上传方式,若使用第三方库可用Axios请求上传
后端文件服务器的几种处理方式:
-
文保存到磁盘目录
文件存取请求直接根据url地址得出文件所在磁盘目录位置,访问速度快
-
文件直接保存到数据库
若文件比较大,影响数据库处理性能,但相比保存到磁盘目录的方法可以确保数据更安全可靠性
-
文件二次请求上传至第三方服务器
后端将接收的表单数据按照第三方接口封装进行请求上传
XMLHttpRequest文件上传
1.表单输入文件
html
<!-- 设置multiple属性时可选择多个文件,可在读取文件时使用onchange监听读取到文件内容后预览、或立即上传,也可以单独用一个按钮事件控制上传 -->
<input type="file" multiple id="fileInput" onchange="onShowTextContent(event)"></input>
<pre id="fileContent"></pre>
<button onclick="uploadFile()">上传文件</button>
预览文本内容并显示方法
html
<script>
function onShowTextContent(event) { // 预览文本内容并显示方法
const file = event.target.files[0];
if (file && file.type.startsWith("text/plain") ) {
const reader = new FileReader();
// 定义文件读取完成后的处理逻辑
reader.onload = function (e) {
const fileContent = e.target.result;
// 显示文本内容
document.getElementById('fileContent').textContent = fileContent;
};
// 读取文件为文本内容
reader.readAsText(file);
} else {
console.error('No file selected');
}
}
</script>
2.XMLHttpRequest上传文件
XMLHttpRequest
对象简记为XHR
,一个内置的浏览器对象,提供简单的方式来发送和接收从 URL 的数据而无需刷新整个页面,这使得它成为 AJAX
(Asynchronous JavaScript and XML)编程的核心技术之一
AJAX
是一种在 Web 应用中通过异步发送 HTTP 请求向服务器获取内容,并使用这些新内容更新页面中相关的部分,而无需重新加载整个页面的 Web 开发技术,这可以让网页更具有响应性,由于这种技术在现代Web开发中太过常见以至于AJAX
这个术语很少被使用到
XMLHttpRequest
具有广泛的兼容性,几乎所有浏览器都支持,包括老版本浏览器,而Fetch API
但在某些旧浏览器(如 IE)中不支持
js
function uploadFile() {
let fileInput = document.getElementById("fileInput"); // 获得input文件输入的引用
let files = fileInput.files;
if (files.length === 0) {
return; // input节点的files是一个文件列表,若 files.length === 0 表示未选择文件,需要跳过
}
let formData = new FormData(); // 创建表单数据对象,封装文件
for (let file of files) {
// file有一些属性常用于校验,如 file.name 文件名称,file.size 文件字节大小,file.type表示文件类型
formData.append("files", file);
}
let request = new XMLHttpRequest(); // 创建一个http请求
let url = "http://127.0.0.1:7999/file/upload";
request.open("POST", url, true); // 参数1为请求方式,参数2为上传服务器地址,参数3取true表示异步
request.onload = function () {
if (request.status === 200) {
console.log("上传完成:", request.responseText);
} else {
console.log("上传出错:", request.statusText);
}
};
request.onerror = function () {
console.log("Error:", request.statusText);
};
request.send(formData); // 发送表单文件数据
}
3. 后端接收文件和保存
先对上传的文件大小、接收文件的类型做判断,然后将原始文件名称、大小、内容类型、存储位置等信息到数据库以提供分类、统计分析
Controller
层接收上传的文件
java
// @RequestParam 注解标注表单的参数名,与form表单一致,若该注解缺省则默认参数名称为"multipart"
// 入参 MultipartFile[] 数组表示接受多个文件,若写为 MultipartFile对象则只会接受第1个文件,cconsumes 指定匹配请求头 "content-type"
@PostMapping(value="/upload/list",consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Map<String,Object> uploadFileList(@RequestParam("files") MultipartFile[] files);
Service
层本地存储文件
java
// controller层调用的文件保存方法,对 MultipartFile[] files 中每个对象处理,判断文件上传个数等
public Map<String,Object> saveMultipartFile(MultipartFile multipartFile) throws IOException {
Map<String, Object> results = new HashMap<>();
if (multipartFile.getSize() >= 1024 * 1024 * 10) {
throw new RuntimeException("文件大小超过了最大限制10M");
}
// multipartFile.getContentType() 获取内容类型
final String oriName = multipartFile.getOriginalFilename();
// 原始文件名不能保证唯一性,需要生成一个唯一文件名,此处仅简单示例,实际开发中文件名称需要确保唯一性
String newName = System.currentTimeMillis() + oriName.substring(oriName.lastIndexOf(".")); //
String fullPath = System.getProperty("user.dir") + "/" + newName;
final InputStream inputStream = multipartFile.getInputStream();
File file = new File(fullPath);
// 也可以直接使用 multipartFile.transferTo(file);
if (file.createNewFile()) {
FileCopyUtils.copy(inputStream, Files.newOutputStream(file.toPath()));
}
// 返回一些有用的信息,若是图片需要返回url等
results.put("url","http://127.0.0.1:7999/file/"+newName);
return results;
}
后端转发请求上传到第三方
java
public Result<FileUploadResponse> uploadImageForward(MultipartFile multipartFile) throws IOException {
MultiValueMap request = new LinkedMultiValueMap(1);
ByteArrayResource byteArrayResource = new ByteArrayResource(multipartFile.getBytes()) {
@Override // 需要覆盖默认的方法,不然无法获取名称
public String getFilename() {
return multipartFile.getOriginalFilename();
}
};
// 此处从multipartFile获取byte[],如果是上传本地文件可以使用io获取byte[]
request.add("fileName", byteArrayResource);
String uploadUrl = this.serverBaseUrl+"/upload"; // 假如这是第三方文件上传地址
FileUploadResponse response = restTemplate.postForObject(uploadUrl, request, FileUploadResponse.class);
return new Result<>(response);
}
4.文件上传大小配置
在 Spring Boot 项目中,可通过配置文件或编程方式来设置文件上传的大小
配置文件方式(application.properties 或 application.yml)
application.properties
properties
# 设置单个文件的最大大小为10MB(默认情况下为1MB)
spring.servlet.multipart.max-file-size=10MB
# 设置整个请求的最大大小为10MB(默认情况下是10MB)
spring.servlet.multipart.max-request-size=10MB
application.yml
yaml
spring:
servlet:
multipart:
max-file-size: 10MB
max-request-size: 10MB
编程方式
可进行更细粒度的控制,可以通过编程配置 MultipartConfigElement
实现
java
@Configuration
public class ServletMultipartConfig {
// 配置文件中添加示例:spring.server.MaxFileSize=1MB
@Value("${spring.server.maxFileSize}")
private DataSize maxFileSize;
// 指定单个文件最大尺寸,如果超过这个大小,后端将不会接受该请求,前端会手到错误:Code Details Error: Request Entity Too Large
@Bean
public MultipartConfigElement multipartConfigElement() {
// log.info("MaxFileSize="+maxFileSize.toMegabytes());
MultipartConfigFactory factory = new MultipartConfigFactory();
factory.setMaxFileSize(maxFileSize); // 但个文件最大大小
factory.setMaxRequestSize(maxFileSize); // 整个请求的最大大小
return factory.createMultipartConfig();
}
}
5.后端跨域请求配置
文件上传在跨域的情况下会上传失败,因此有必要设置服务器的跨域请求规则
springboot的配置类中添加跨域请求配置,以拦截器方式实现为例:
java
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Bean
public HandlerInterceptor accessInterceptor() {
return new HandlerInterceptor(){
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 若请求头设置了Access-Control-Allow-Credentials为true,那"Access-control-Allow-Origin不能使用通配符*,而必须要使用确切的值
response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH");
// 若前端传送了自定义的请求头而没有在这里面设置,某些情况下则会提示跨域请求错误:not allowed by Access-Control-Allow-Headers in preflight response.
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true"); // 前端请求设置credentials: 'include', 这里就必须设置为true
response.setHeader("Access-Control-Max-Age", "3600");
// 跨域时会首先发送一个option请求,这里我们给option请求直接返回正常状态
if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {
response.setStatus(HttpStatus.OK.value());
return false;
}
return true;
}
};
}
@Override
public void addInterceptors(InterceptorRegistry registry) { // 添加拦截器
registry.addInterceptor(this.accessInterceptor())
.excludePathPatterns("/error")
.excludePathPatterns("/webjars/**", "/v2/api-docs/**");
}
}
与上述拦截器实现的效果一样,也可以采用过滤器实现跨域请求配置:
java
@Bean // 注册过滤器
public FilterRegistrationBean<CustomCorsFilter> customCorsFilterFilterRegistrationBean(){
FilterRegistrationBean<CustomCorsFilter> registrationBean = new FilterRegistrationBean<>(this.customCorsFilter());
registrationBean.setOrder(-1); // 设置序号,越小优先级越高越靠前执行,确保在其他业务处理之前执行到
registrationBean.addUrlPatterns("/*");
return registrationBean;
}
@Bean // 确保过滤器被加入到ioc容器
public CustomCorsFilter customCorsFilter(){
return new CustomCorsFilter();
}
// 实现一个过滤器,继承Filter接口,覆盖doFilter方法
public static class CustomCorsFilter implements Filter{
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse response = (HttpServletResponse) servletResponse;
HttpServletRequest request = (HttpServletRequest) servletRequest;
response.setHeader("Access-control-Allow-Origin", request.getHeader("Origin"));
response.setHeader("Access-Control-Allow-Methods", "GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH");
// 若前端传送了自定义的请求头而没有在这里面设置,某些情况下则会提示跨域请求错误:not allowed by Access-Control-Allow-Headers in preflight response.
response.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
response.setHeader("Access-Control-Allow-Credentials", "true"); // 前端请求设置credentials: 'include', 这里就必须设置为true
response.setHeader("Access-Control-Max-Age", "3600"); // 设置 Access-Control-Max-Age 为 3600,表示预检请求的结果可以缓存 3600 秒(即 1 小时),在此期间浏览器不需要为相同的跨域请求再次发送预检请求
filterChain.doFilter(servletRequest,servletResponse);
}
}
Axios文件上传
Axios
是一个基于promise的http库,可以用于Node.js中,可兼容所有主流浏览器,该库提供了简洁的API用于执行Http请求,可拦截请求和响应,转换请求和响应请求数据等
引入axios库,然后使用axios提供的方法进行文件上传,后端接收文件的方法与上述一致
html
<!-- 引入axios库,实际开发中建议用 npm install axios 安装到本地 -->
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>
<script>
function uploadFileByAxios() {
const files = document.getElementById('fileInput').files;
const formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]);
}
let httpHeaders = {};
// 表单Content-Type可以不用设置,默认就是 httpHeaders['Content-Type'] = 'multipart/form-data';
// let uploadUrl = "http://127.0.0.1:7999/file/upload";
axios.post(uploadUrl, formData, { headers: httpHeaders })
.then(response => {
console.log('Files uploaded successfully:', response.data);
})
.catch(error => {
console.error('Error uploading files:', error);
});
}
</script>
Fetch API实现文件上传
Fetch API
是一个用于现代Web应用执行网络请求的内置接口,它在XMLHttpRequest
之后出现
Fetch API
基于 Promise,可以使用链式结构来处理请求和响应,它提供了一种更简单、更灵活的方式来进行异步请求
js
function uploadFilesByFetch() {
let files = document.getElementById('fileInput').files;
let formData = new FormData();
for (let i = 0; i < files.length; i++) {
formData.append('files', files[i]);
}
let uploadUrl = uploadUrl = "http://127.0.0.1:7999/file/upload";
fetch(uploadUrl, {
method: 'POST',
body: formData
})
.then(response => {
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
return response.json();
})
.then(data => {
console.log('Files uploaded successfully:', data);
})
.catch(error => {
console.error('Error uploading files:', error);
});
}
下载文件
下载文件包括交互事件请求下载、超链接下载
XMLHttpRequest文件下载
1.下载按钮事件
html
<div>
<label>下载地址:</label>
<input type="text" id="downloadUrl" value="http://127.0.0.1:7999/file/download/filename"></input>
</div>
<button onclick="downloadFileByXHR()">XMLHttpRequest下载</button>
<!-- 可以隐藏一个a标签,通过其他按钮事件触发 -->
<button onclick="downloadLink()">超链接下载方式</button><a id="link" style="display: none;"></a>
<!-- 超链接下载方法:直接指定带有href属性的a标签点击即可下载 -->
<a href="http://127.0.0.1:7999/file/download/24071411085841900.txt" download="filename.txt">点击下载文件</a>
2.XMLHttpRequest下载文件
关键是设置responseType,以及对响应数据二进制数据处理
js
function downloadFileByXHR() {
let downloadUrl = document.getElementById("downloadUrl").value; // 下载的url地址
// let downloaduUrl = "http://192.168.95.59:7999/download/file/"+fileName;
let request = new XMLHttpRequest();
request.open("GET", downloadUrl, true);
// 响应被处理为 Blob 对象,可以通过 `xhr.response` 访问响应数据,适用于处理二进制数据,如文件、图像等
request.responseType = "blob";
request.onload = function () {
if (request.status === 200) {
let contentType = request.getResponseHeader("Content-Type");
//验证响应响应数据,若异常则响应 Content-Type 为 json 认定为下载失败
if (contentType && contentType.startsWith('application/json')) {
const reader = new FileReader();
reader.onload = function () {
const text = reader.result; // 下载出错时后端返回的错误信息
console.log("text:", text);
};
reader.readAsText(request.response); // 尝试将blob数据读取为文本数据
return;
}
let disposition = request.getResponseHeader("Content-Disposition");
// Content-Disposition: attachment; filename=24070701440188300.png
const data = new Blob([request.response], { type: request.getResponseHeader("Content-Type") });
const url = window.URL.createObjectURL(data);
const link = document.createElement("a");
link.href = url;
let fileName = getFileName(disposition);
if (fileName !== "") {
link.download = fileName;
}
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
window.URL.revokeObjectURL(url); // 释放内存
link.remove();
}
};
request.onerror = function () {
console.low("error:", request.responseText);
};
request.send();
}
链接下载方法
js
// 设置隐藏表单下载链接,点击即可下载,这种方法不够友好,若后端错误了前端直接暴露原始错误信息
function downloadLink() {
console.log("链接下载");
let link = document.getElementById("link");
link.href = "http://127.0.0.1:7999/file/download/24071415521018500.txt";
// link.download = "test.txt"; // 可指定下载后的名称
link.click();
}
3.后端文件下载处理
Controller层接收文件下载请求
@ApiOperation(value = "下载文件")
@RequestMapping(value = "/download/{fileName}", method = RequestMethod.GET)
ResponseEntity<byte[]> getDownloadFileResponse(@PathVariable("fileName") String fileName);
Service层读取文件
java
// 给前端响应一个字节数组,使用ResponseEntity封装以便设置HttpHeaders
public ResponseEntity<byte[]> getDownloadFileResponse(String fileName) {
byte[] imageBytes = this.getLocalFile(fileName); // 获取文件的字节数组
String contentType = getFileMediaType(fileName).toString();
try {
fileName = URLEncoder.encode(fileName, "UTF-8"); // 中文字符串编码处理避免前端收到乱码
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
HttpHeaders headers = new HttpHeaders();
// headers.add("Custom-Action", "download");
headers.add(HttpHeaders.CONTENT_TYPE, contentType); // 设置Content-Type
headers.add(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=" + fileName); // 设置附件,前端点击超链接下载时该名称将作为下载的文件名称
headers.add("Access-Control-Expose-Headers", "Content-Type,Content-Disposition,Custom-Action");
return new ResponseEntity<>(imageBytes, headers, HttpStatus.OK);
}
// 读取磁盘指定文件为字节数组
public byte[] getLocalFile(String fileName) {
File file = new File(localFileProperties.getPath() + "/" + fileName); // 服务器文件位置
FileInputStream inputStream = null;
try {
inputStream = new FileInputStream(file);
byte[] bytes = new byte[inputStream.available()];
inputStream.read(bytes, 0, inputStream.available());
return bytes;
} catch (IOException e) {
e.printStackTrace();
log.error("获取文件失败:",e);
throw new RuntimeException("找不到指定的文件");
}
}
// 根据文件名后缀确定图片的 MIME 类型
public MediaType getFileMediaType(String imageName) {
String fileExtension = imageName.substring(imageName.lastIndexOf(".") + 1).toLowerCase();
switch (fileExtension) {
case "jpeg":
case "jpg":
return MediaType.IMAGE_JPEG;
case "png":
return MediaType.IMAGE_PNG;
case "gif":
return MediaType.IMAGE_GIF;
case "bmp":
return MediaType.parseMediaType("image/bmp");
default:
return MediaType.APPLICATION_OCTET_STREAM; // 默认为二进制流
}
}
4.后端外链请求方法
Controller层接收一个下载外链的请求
java
@ApiOperation(value = "第三方外链下载")
@RequestMapping(value = "/outlink/download", method = RequestMethod.GET)
ResponseEntity<byte[]> getDownloadOutlinkResponse(@RequestParam("url") String url);
Service层读取第三方服务器的文件
这里为啥前端不直接访问第三方文件服务器,因为在一些业务场景中,前端可能没有权限访问直接访问、直接访问不安全或者在对第三方外链的访问情况做分析统计时需要
java
public ResponseEntity<byte[]> downloadOutLink(String fileUrl) {
log.info("downloadOutLink fileUrl:{}", fileUrl);
RestTemplate restTemplate = new RestTemplate();
ResponseEntity<byte[]> responseEntity = restTemplate.exchange(fileUrl, HttpMethod.GET, null, byte[].class);
byte[] bytes = responseEntity.getBody();
log.info("return entity...");
HttpHeaders oldHeaders = responseEntity.getHeaders();
oldHeaders.entrySet().forEach(entry -> {
log.info("headName:{}, headValue:{}", entry.getKey(), entry.getValue());
});
String contentDisposition = oldHeaders.getFirst(HttpHeaders.CONTENT_DISPOSITION);
String contentType = oldHeaders.getFirst(HttpHeaders.CONTENT_TYPE);
HttpHeaders responseHeaders = new HttpHeaders();
// 新创建一个 responseHeaders 用于设置返回前端的请求头,此处没有直接使用oldHeaders,因为这样可能会暴露出现某些不被允许的请求头以至于出错
if (contentDisposition != null && !Objects.equals("", contentDisposition)) {
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);
} else {
// 获取一个文件扩展名
String fileSuffix = "";
if(fileUrl.contains(".")){
String suffix = fileUrl.substring(fileUrl.lastIndexOf(".")+1).toLowerCase();
if(suffix.length()>0 && suffix.length()<5){
fileSuffix = suffix;
}
}
if(Objects.equals(fileSuffix,"")){
fileSuffix = "download";
}
String fileName = System.currentTimeMillis()+"."+fileSuffix;
responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, String.format("attachment; filename=%s",fileName));
}
responseHeaders.add(HttpHeaders.CONTENT_TYPE, contentType);
return ResponseEntity.ok()
.headers(responseHeaders)
.body(bytes);
}
Fetch API下载文件
java
function downloadFileByFetch() {
// const url = 'http://127.0.0.1:7999/file/upload'; // 文件的URL
let url = document.getElementById("downloadUrl").value;
fetch(url)
.then(async (response) => {
console.log("response:", response);
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const contentType = response.headers.get('Content-Type');
// 后端抛出异常时,返回json数据
if(contentType && contentType.startsWith("application/json")){
const jsonResponse = await response.json();
if (jsonResponse.code === 1) {
// 后端返回了统一错误响应
console.error('Request failed with message:', jsonResponse.msg);
showInfo(JSON.stringify(jsonResponse));
return;
}
}
console.log("contentType:",contentType);
const disposition = response.headers.get('Content-Disposition');
console.log("disposition:", disposition);
let fileName = "unknown.download";
if (disposition !== null) {
const fileNameMatch = disposition.match(/filename="?([^"]+)"?/);
if (fileNameMatch.length === 2) {
fileName = fileNameMatch[1];
}
fileName = fileName.replace(/\+/g, ' ');
fileName = decodeURI(fileName);
}
response.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); // 释放URL对象
});
})
.catch(error => {
console.error('error:', error);
});
}
Axios下载文件
js
const downloadFileByAxios = () => {
// 使用 Axios 发起 GET 请求
// let url = "http://192.168.95.59:7999/download/file/xxx";
let url = document.getElementById("downloadUrl").value;
let httpHeaders = {};
// httpHeaders = { "Custom-Action": "RQ-download" };
axios.get(url, { responseType: 'blob', headers: httpHeaders })
.then(response => {
// 请求成功处理
console.log("收到下载数据:", response);
// let res = response.data;
handleAxiosDownloadResponse(response.data, response.headers);
}).catch(error => {
// 请求失败处理
console.error('Error fetching posts:', error);
});
}
// 处理axios下载的响应文件
const handleAxiosDownloadResponse = async (data, headers) => {
const contentType = headers['content-type'];
// 后端返回的不是文件而是json对象,判断为异常
if (contentType && contentType.startsWith("application/json")) {
// data.text()返回一个Promise对象,因此要使用await,此时方法使用使用async关键字标识为异步方法
let text = await data.text();
console.log("text:", text);
const jsonObj = JSON.parse(text);
if (jsonObj.code === 1) {
console.log("请求异常:", text);
}
showInfo(text);
return;
}
const blob = new Blob([data], { type: headers['content-type'] });
// 创建一个指向 Blob 对象的 URL
const url = window.URL.createObjectURL(blob);
// 创建一个 <a> 元素,并设置其 href 属性指向 Blob URL
const a = document.createElement('a');
a.href = url;
// 从响应头或其他方式获取文件名
const contentDisposition = headers['content-disposition'];
console.log("content-disposition:", contentDisposition)
let fileName = '';
if (contentDisposition) {
const fileNameMatch = contentDisposition.match(/filename="?([^"]+)"?/);
if (fileNameMatch.length === 2) {
fileName = fileNameMatch[1];
}
fileName = fileName.replace(/\+/g, ' '); // 将符号+替换为空格
fileName = decodeURI(fileName);
// console.log("fileName:",fileName);
}
a.download = fileName; // 设置下载文件名
// 将 <a> 元素添加到 DOM 并触发点击事件
document.body.appendChild(a);
a.click();
// 移除 <a> 元素并释放 Blob URL
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
}
Axios读取文件内容
js
const readFileContentByAxios = () => {
// 使用 Axios 发起 GET 请求
// let url = "http://192.168.95.59:7999/download/file/xxx";
let url = document.getElementById("downloadUrl").value;
let httpHeaders = {};
// responseType选项取值与XMLHttpRequest的responseType一样,指定为arraybuffer,表示响应被处理为 ArrayBuffer 对象,适用于处理原始二进制数据
axios.get(url, { responseType: 'arraybuffer', headers: httpHeaders })
.then(async (response) => {
// 请求成功处理
console.log("收到数据:", response);
// let res = response.data;
const text = new TextDecoder('utf-8').decode(new Uint8Array(response.data));
console.log(text);
})
.catch(error => {
// 请求失败处理
console.error('Error fetching posts:', error);
});
}