ajax上传、下载文件

news/2024/11/6 22:23:34/文章来源:https://www.cnblogs.com/94pm/p/18531157

一、上传

1.上传数据的封装

在上传文件时,最常用的方式是使用 FormData 对象,它会自动将请求头中的 Content-Type 请求头指定为multipart/form-data

const formData = new FormData();
formData.append("file", fileInput.files[0]);  // fileInput 是 <input type="file">const xhr = new XMLHttpRequest();
xhr.open("POST", "/upload");// 不要设置 Content-Type,浏览器会自动生成类似于 multipart/form-data 的头
xhr.send(formData);

2.上传进度条

XMLHttpRequest 对象的 upload 属性有一个 progress 事件,它可以获取文件上传的进度。每当文件上传进度更新时,都会触发该事件,提供已上传字节数和文件总大小的信息

    const xhr = new XMLHttpRequest();xhr.open("POST", "/upload");  // 替换为实际的上传 URL// 监听上传进度事件xhr.upload.addEventListener("progress", (event) => {if (event.lengthComputable) {const percentComplete = (event.loaded / event.total) * 100;const progressBar = document.getElementById("progress-bar");progressBar.style.width = percentComplete + "%";progressBar.textContent = Math.round(percentComplete) + "%";}});// 上传完成事件xhr.onload = () => {if (xhr.status === 200) {alert("上传成功!");} else {alert("上传失败!");}};// 发送表单数据xhr.send(formData);

3.大文件上传

文件分片上传的关键技术主要涉及文件分片、分片上传、分片管理和服务器端的分片合并。这些技术通过优化传输和可靠性,确保大文件在网络不稳定或需要续传的场景下能够顺利完成上传。以下是文件分片上传的关键技术点:

文件分片(Chunking)

  • 技术点:文件分片是将大文件按固定大小(通常为 1 MB 或 5 MB)划分为若干小块(Chunk),这些分片可以单独处理和上传。通过 JavaScript 的 Blob.slice 方法可以轻松实现分片。
  • 实现细节:在前端使用 Blob.slice(start, end) 方法获取每个分片,通过循环逐片读取并存储到 FormData 中。

分片上传与进度管理

  • 技术点:每个分片单独发起上传请求(如使用 XMLHttpRequestfetch),通过监听上传进度事件来实时更新上传进度。同时,可以记录每个分片的状态,便于上传失败时进行重试。
  • 并发上传:对于大文件,可以并发上传多个分片(Promise.all),提高上传速度。例如,前端可以控制同时上传 3 个分片,并在其中一个分片完成后再上传下一个分片。
  • 并发控制:通过控制并发数,既保证上传速度,又防止过多并发导致服务器压力过大。

分片标识与校验

  • 分片标识:每个分片通常使用唯一标识(如文件名、分片序号、总分片数)来标识,可以在请求中带上这些信息,方便服务器识别每个分片的位置。
  • 分片校验:为了确保每个分片正确上传,可以对每个分片计算哈希值(如 MD5、SHA-256),服务器上传后进行校验。这样可以在上传失败或分片出错时重试,保证数据完整性。

服务端分片接收与校验

  • 接收分片:服务器根据上传请求中的分片标识(如 chunkIndextotalChunks)存储每个分片,通常放在临时文件夹中。
  • 校验分片:每个分片上传后可以计算哈希并与客户端传来的哈希比对,确保分片未损坏。校验通过后才认为分片上传成功,失败则需要重新上传该分片。

服务端合并分片

  • 合并触发:在服务器端收到所有分片后,可以触发文件合并。通常通过 chunkIndextotalChunks 判断是否收到完整文件。
  • 合并顺序:合并时需要按照 chunkIndex 顺序将所有分片按序读入并写入完整文件。
  • 并发与异步:合并过程可以使用多线程或异步方法,尤其是当文件较大时,可以提升合并效率。
  • 清理临时文件:合并完成后需要删除分片的临时文件,释放服务器存储空间。

断点续传支持

  • 断点记录:断点续传的关键在于记录已上传的分片。前端可以在本地存储(如 localStorage)中记录已成功上传的分片序号。
  • 续传实现:当上传中断时,用户可以从记录的进度重新开始上传,无需重复上传已完成的分片。这可以减少带宽浪费和上传时间。

7.实现代码

客户端

  const CHUNK_SIZE = 1024 * 1024; // 每片1MBasync function uploadFile() {const fileInput = document.getElementById("fileInput");const file = fileInput.files[0];if (!file) {alert("请选择文件后再上传!");return;}const totalChunks = Math.ceil(file.size / CHUNK_SIZE);  // 计算分片数量let uploadedChunks = 0;for (let start = 0; start < file.size; start += CHUNK_SIZE) {const end = Math.min(start + CHUNK_SIZE, file.size);const chunk = file.slice(start, end); // 获取文件分片const chunkIndex = start / CHUNK_SIZE; // 当前分片序号// 使用 FormData 上传每个分片const formData = new FormData();formData.append("file", chunk); // 添加分片数据formData.append("filename", file.name); // 添加文件名formData.append("chunkIndex", chunkIndex); // 添加分片序号formData.append("totalChunks", totalChunks); // 添加总分片数// 上传当前分片try {
//等待当前task完成后再进入下一个循环await uploadChunk(formData);uploadedChunks++;updateProgress(uploadedChunks, totalChunks); // 更新进度条} catch (error) {console.error(`分片 ${chunkIndex} 上传失败`, error);return; // 上传失败退出}}alert("文件上传成功!");}function uploadChunk(formData) {return new Promise((resolve, reject) => {const xhr = new XMLHttpRequest();xhr.open("POST", "/upload-chunk"); // 替换为实际的上传 URLxhr.onload = () => {if (xhr.status === 200) {resolve();} else {reject(new Error("分片上传失败"));}};xhr.onerror = () => reject(new Error("网络错误"));xhr.send(formData);});}// 更新进度条function updateProgress(uploadedChunks, totalChunks) {const progressBar = document.getElementById("progress-bar");const percentComplete = (uploadedChunks / totalChunks) * 100;progressBar.textContent = `上传进度: ${Math.round(percentComplete)}%`;} 

服务端

在所有分片上传完毕后,服务器端会收到所有分片并合并。服务器可以根据 filenamechunkIndex 来识别每个分片,并按顺序合并它们。

 

const fs = require("fs");
const path = require("path");
const express = require("express");
const app = express();
const uploadDir = "uploads/"; // 保存分片的临时目录app.post("/upload-chunk", (req, res) => {const { filename, chunkIndex, totalChunks } = req.body;const chunk = req.files.file; // 获取上传的分片文件// 将每个分片存储到临时文件夹const chunkPath = path.join(uploadDir, `${filename}.part${chunkIndex}`);fs.writeFileSync(chunkPath, chunk.data);// 检查是否所有分片都上传完成if (parseInt(chunkIndex) === totalChunks - 1) {// 合并所有分片const filePath = path.join(uploadDir, filename);const writeStream = fs.createWriteStream(filePath);for (let i = 0; i < totalChunks; i++) {const partPath = path.join(uploadDir, `${filename}.part${i}`);const data = fs.readFileSync(partPath);writeStream.write(data);fs.unlinkSync(partPath); // 删除已合并的分片}writeStream.end();res.send("文件上传成功并合并完成!");} else {res.send("分片上传成功!");}
});app.listen(3000, () => {console.log("Server started on http://localhost:3000");
});

 

二、下载

1. 使用 <a> 标签的 download 属性

这是实现文件下载最简单的方式。<a> 标签的 download 属性可以强制浏览器下载文件而不是直接打开文件。

<a href="path/to/file.pdf" download="filename.pdf">下载文件</a>
当需要下载的数据是由 JavaScript 动态生成时,可以将数据转换为 Blob 对象,并生成一个 URL 来进行下载。使用 Blob 和 URL.createObjectURL 生成下载链接
function downloadBlob(data, filename, mimeType) {const blob = new Blob([data], { type: mimeType });const url = URL.createObjectURL(blob);const a = document.createElement("a");a.href = url;a.download = filename;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(url); // 释放 URL
}// 使用示例
const data = "Hello, this is a text file!";
downloadBlob(data, "example.txt", "text/plain");

2.使用ajax和 Blob 实现文件下载

不推荐使用该方法。下载速度比方法1要慢,要等ajax接收完文件再调用浏览器的文件保存api,文件较大时点击后可能需要很长时间才有反应,并且下载传输过程中出现问题后,无法断点续传。

async function downloadFile(url, filename) {const response = await fetch(url);const blob = await response.blob();const a = document.createElement("a");a.href = URL.createObjectURL(blob);a.download = filename;document.body.appendChild(a);a.click();document.body.removeChild(a);URL.revokeObjectURL(a.href);
}// 使用示例
downloadFile("path/to/file.pdf", "downloaded_file.pdf");

  

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/827900.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

基于ACO蚁群优化的VRPSD问题求解matlab仿真,输出规划路径结果和满载率

1.程序功能描述 基于ACO蚁群优化的VRPSD问题求解matlab仿真,输出ACO优化的收敛曲线,规划路径结果和每一条路径的满载率。 2.测试软件版本以及运行结果展示MATLAB2022a版本运行 3.核心程序%搜索 for i=1:Iteration iis_best=0; for j=1:Npop%蚂蚁搜索一次Ants …

六自由度Stewart控制系统matlab仿真,带GUI界面

1.课题概述六自由度Stewart平台控制系统是一种高精度、高稳定性的运动模拟装置,广泛应用于飞行模拟、汽车驾驶模拟、虚拟现实、精密定位等领域。其工作原理基于Stewart机构(也称为并联机构)的设计理念,通过六个独立的线性致动器(通常为液压缸或电动推杆)连接固定基座与移…

20241106-语音视频转文字的测试及其价格

一、绘影Ai字幕价格 个人专业版本60元, 商用大约一个120元,支持批量。 二、手机app 讯飞语音、语音转文字等 讯飞听见迅捷转语音 https://www.bilibili.com/read/cv21023516/ 功能 价格 视频、语音转文字的原理 涉及的模型 感悟: 我使用33字幕本地识别,实在太废时间了。我…

javascript模块 (Module) 简介

https://blog.csdn.net/chehec2010/article/details/119804381随着ES6的出现,js模块已经成为正式的标准了。曾经为了解决js模块问题而发展起来的民间秘籍,requireJs(AMD)、SeaJs(CMD)、Node(CommonJs),已经或者不久的将来会成为历史。了解历史也是很重要的,因为正式标准就是…

11.6虎牙客户端一面

1.自我介绍 2.共享屏幕看代码 ​ 类内不能用auto推导成员变量类型 ​ 类内的成员函数调用应该用作用域 3.struct和class区别 相同点两者都拥有成员函数、公有和私有部分 任何class能完成的工作,struct同样能完成不同点如果不指定,struct默认公有,class默认私有 class默认pri…

helm-v3-新的功能

helm-v3-新的功能 Helm 是什么? Helm 是一个命令行下的客户端工具。主要用于 Kubernetes 应用程序 Chart 的创建、打包、发布以及创建管理本地或者远程的Chart仓库。 Helm 解决什么痛点?如何统一管理、配置和更新分散的k8s yaml资源文件 如何分发和复用一套应用模板 如何将应…

基于Java+SpringBoot心理测评心理测试系统功能实现四

三、部分系统功能心理测试信息数据层Dao、试题题型信息数据层Dao、试题信息数据层Dao、角色信息数据层Dao、用户信息数据层Dao、前台用户信息数据层Dao、咨询师预约信息数据层Dao、咨询师信息数据层Dao、试卷信息数据层Dao、试卷答案信息数据层Dao一、前言介绍: 1.1 项目摘要 …

七、Spring Boot集成Spring Security之前后分离认证最佳实现

前后分离项目认证流程代码实现,基于spring security原生流程设计,实现RESTFUL接口、分布式缓存二、自定义用户名密码认证过滤器RestfulUsernamePasswordAuthenticationFilter 1、注册过滤器方式使用httpSecurity.addFilter/addFilterBefore/addFilterAfter向过滤器链中添加过…

WPF Slider控件 划一划

WPF Slider控件 划一划Slider控件就是常见的滑块。 控件的外观一般显示一系列刻度,并存在一个可以拖动的滑块,可以通过拖动滑块来控制控件的值。如Windows11的音量控制。 做一个简单的滑块控件,并将滑块的值显示在文本框中。<StackPanel Margin="10"><!…

5种AI合同审查方法,免费开源,提升50%法律文件比对效率

一、系统概述 在法律行业,合同和法律文件的版本审查是确保合规与降低风险的重要步骤。然而,手动对比文档各版本间的差异不仅耗时,且易产生误判。思通数科的AI多模态能力平台专为法律文件审核设计,结合了先进的Diff算法与Levenshtein距离算法,支持字符、单词、段落的精确对…

Markdown编辑入门

Markdown编辑入门 标题 三级标题 四级标题 字体 Hello,world Hello,world Hello,world ~~Hello,world ~~ 引用十步杀一人,千里不留行。分割线图片列表ABC123链接 百度 表格姓名 年龄 性别张三 18 男代码 <html>

基于Azure DevOps 的 CICD 项目部署(.Net Core)

使用微软的 来进行CICD 链接:https://dev.azure.com 创建新项目3.创建项目名称4.选择仓库地址5.选择空模板6.创建代理池7.按照以下步骤把代理部署到服务器上8.连接你的服务器 9.创建新的文件夹 mkdir myange cd myagent 10.可通过链接下载文件 wget https://vstsagentpackage.…