前端文件相关总结

news2024/10/7 12:23:17

  先引用掘金上的一个总结,将前端会遇到的文件相关的知识点间的关系串联了起来。

  前端技术提供了一些高效的解决方案:文件流操作和切片下载与上传。

1. 文件基本操作

1.1 数据流和文件处理的基本概念

数据流是指连续的数据序列,可以从一个源传输到另一个目的地。在前端开发中,文件可以被看作数据流的一种形式,可以通过数据流的方式进行处理。 文件处理涉及读取和写入文件的操作,包括读取文件的内容、写入数据到文件,以及对文件进行删除、重命名等操作。

1.2. Blob 对象和 ArrayBuffer:处理二进制数据

在前端处理文件时,经常需要处理二进制数据。Blob(Binary Large Object)对象是用来表示二进制数据的一个接口,可以存储大量的二进制数据。Blob 对象可以通过构造函数进行创建,也可以通过其他 API 生成,例如通过 FormData 对象获取上传的文件。 而 ArrayBuffer 是 JavaScript 中的一个对象类型,用于表示一个通用的、固定长度的二进制数据缓冲区。我们可以通过 ArrayBuffer 来操作和处理文件的二进制数据。

示例代码:

 1 import React, { useState } from 'react';
 2 
 3 function FileInput() {
 4   const [fileContent, setFileContent] = useState('');
 5 
 6   // 读取文件内容到ArrayBuffer
 7   function readFileToArrayBuffer(file) {
 8     return new Promise((resolve, reject) => {
 9       const reader = new FileReader();
10 
11       // 注册文件读取完成后的回调函数
12       reader.onload = function(event) {
13         const arrayBuffer = event.target.result;
14         resolve(arrayBuffer);
15       };
16 
17       // 读取文件内容到ArrayBuffer
18       reader.readAsArrayBuffer(file);
19     });
20   }
21 
22   // 将ArrayBuffer转为十六进制字符串
23   function arrayBufferToHexString(arrayBuffer) {
24     const uint8Array = new Uint8Array(arrayBuffer);
25     let hexString = '';
26     for (let i = 0; i < uint8Array.length; i++) {
27       const hex = uint8Array[i].toString(16).padStart(2, '0');
28       hexString += hex;
29     }
30     return hexString;
31   }
32 
33   // 处理文件选择事件
34   function handleFileChange(event) {
35     const file = event.target.files[0];  // 获取选中的文件
36 
37     if (file) {
38       readFileToArrayBuffer(file)
39         .then(arrayBuffer => {
40           const hexString = arrayBufferToHexString(arrayBuffer);
41           setFileContent(hexString);
42         })
43         .catch(error => {
44           console.error('文件读取失败:', error);
45         });
46     } else {
47       setFileContent('请选择一个文件');
48     }
49   }
50 
51   return (
52     <div>
53       <input type="file" onChange={handleFileChange} />
54       <div>
55         <h4>文件内容:</h4>
56         <pre>{fileContent}</pre>
57       </div>
58     </div>
59   );
60 }
61 
62 export default FileInput;

1.3 使用 FileReader 进行文件读取

FileReader 是前端浏览器提供的一个 API,用于读取文件内容。通过 FileReader,我们可以通过异步方式读取文件,并将文件内容转换为可用的数据形式,比如文本数据或二进制数据。 FileReader 提供了一些读取文件的方法,例如 readAsText()、readAsArrayBuffer() 等,可以根据需要选择合适的方法来读取文件内容。

1.4 将文件流展示在前端页面中

一旦我们成功地读取了文件的内容,就可以将文件流展示在前端页面上。具体的展示方式取决于文件的类型。例如,对于文本文件,可以直接将其内容显示在页面的文本框或区域中;对于图片文件,可以使用 <img> 标签展示图片;对于音视频文件,可以使用 <video> 或 <audio> 标签来播放。 通过将文件流展示在前端页面上,我们可以实现在线预览和查看文件内容的功能。

好的,这一部分就基本介绍完毕,总结一下。前端文件操作流是处理大型文件的一种常见方式,他可以通过数据流的方式对文件进行操作。Blob对象 和 ArrayBuffer是处理二进制数据的重要工具。而FileReader则是读取文件内容的的关键组件。通过这些技术,我们可以方便的在前端页面上进行操作或者文件展示。

2. 文件切片

流程:

A(开始) --> B{选择文件}
B -- 用户选择文件 --> C[切割文件为多个切片]
C --> D{上传切片}
D -- 上传完成 --> E[合并切片为完整文件]
E -- 文件合并完成 --> F(上传成功)
D -- 上传中断 --> G{保存上传进度}
G -- 上传恢复 --> D
G -- 取消上传 --> H(上传取消)

  传统文件整体上传下载的弊端:

  等待较长、网络占用、续传困难。

  切片上传下载好处:

  • 快速启动:客户端可以快速开始下载,因为只需要下载第一个切片即可。
  • 并发下载:通过使用多个并发请求下载切片,可以充分利用带宽,并提高整体下载速度。
  • 断点续传:如果下载中断,客户端只需要重新下载中断的切片,而不需要重新下载整个文件。

2.1 切片上传-借助FormData

 1 const [selectedFile, setSelectedFile] = useState(null); 
 2 const [progress, setProgress] = useState(0);
 3  // 处理文件选择事件
 4  function handleFileChange(event) {
 5    setSelectedFile(event.target.files[0]);
 6  }
 7 
 8  // 处理文件上传事件
 9  function handleFileUpload() {
10    if (selectedFile) {
11      // 计算切片数量和每个切片的大小
12      const fileSize = selectedFile.size;
13      const chunkSize = 1024 * 1024; // 设置切片大小为1MB
14      const totalChunks = Math.ceil(fileSize / chunkSize);
15 
16      // 创建FormData对象,并添加文件信息
17      const formData = new FormData();
18      formData.append('file', selectedFile);
19      formData.append('totalChunks', totalChunks);
20 
21      // 循环上传切片
22      for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
23        const start = chunkNumber * chunkSize;
24        const end = Math.min(start + chunkSize, fileSize);
25        const chunk = selectedFile.slice(start, end);
26        formData.append(`chunk-${chunkNumber}`, chunk, selectedFile.name);
27      }
28 
29      // 发起文件上传请求
30      axios.post('/upload', formData, {
31        onUploadProgress: progressEvent => {
32          const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
33          setProgress(progress);
34        }
35      })
36        .then(response => {
37          console.log('文件上传成功:', response.data);
38        })
39        .catch(error => {
40          console.error('文件上传失败:', error);
41        });
42    }
43  }

2.2 切片下载

实现客户端切片下载的基本方案如下:

  1. 服务器端将大文件切割成多个切片,并为每个切片生成唯一的标识符。
  2. 客户端发送请求获取切片列表,同时开始下载第一个切片。
  3. 客户端在下载过程中,根据切片列表发起并发请求下载其他切片,并逐渐拼接合并下载的数据。
  4. 当所有切片都下载完成后,客户端借助blob将下载的数据合并为完整的文件。
 1 function downloadFile() {
 2   // 发起文件下载请求
 3   fetch('/download', {
 4     method: 'GET',
 5     headers: {
 6       'Content-Type': 'application/json',
 7     },
 8   })
 9     .then(response => response.json())
10     .then(data => {
11       const totalSize = data.totalSize;
12       const totalChunks = data.totalChunks;
13 
14       let downloadedChunks = 0;
15       let chunks = [];
16 
17       // 下载每个切片
18       for (let chunkNumber = 0; chunkNumber < totalChunks; chunkNumber++) {
19         fetch(`/download/${chunkNumber}`, {
20           method: 'GET',
21         })
22           .then(response => response.blob())
23           .then(chunk => {
24             downloadedChunks++;
25             chunks.push(chunk);
26 
27             // 当所有切片都下载完成时
28             if (downloadedChunks === totalChunks) {
29               // 合并切片
30               const mergedBlob = new Blob(chunks);
31 
32               // 创建对象 URL,生成下载链接
33               const downloadUrl = window.URL.createObjectURL(mergedBlob);
34 
35               // 创建 <a> 元素并设置属性
36               const link = document.createElement('a');
37               link.href = downloadUrl;
38               link.setAttribute('download', 'file.txt');
39 
40               // 模拟点击下载
41               link.click();
42 
43               // 释放资源
44               window.URL.revokeObjectURL(downloadUrl);
45             }
46           });
47       }
48     })
49     .catch(error => {
50       console.error('文件下载失败:', error);
51     });
52 }

2.3 显示下载进度和完成状态

为了显示下载进度和完成状态,可以在客户端实现以下功能:

  1. 显示进度条:客户端可以通过监听每个切片的下载进度来计算整体下载进度,并实时更新进度条的显示。
  2. 显示完成状态:当所有切片都下载完成后,客户端可以显示下载完成的状态,例如显示一个完成的图标或者文本。

这里我们可以继续接着切片上传代码示例里的继续写。

  • 当用户点击下载按钮时,通过 handleFileDownload 函数处理文件下载事件。
  • 在 handleFileDownload 函数中,使用 axios 库发起文件下载请求,并设置 responseType: 'blob' 表示返回二进制数据。
  • 通过监听 onDownloadProgress 属性获取下载进度,并更新进度条的显示。
  • 下载完成后,创建一个临时的 URL 对象用于下载,并通过动态创建 <a> 元素模拟点击下载。
 1 function handleFileDownload() {
 2     axios.get('/download', {
 3       responseType: 'blob',
 4       onDownloadProgress: progressEvent => {
 5         const progress = Math.round((progressEvent.loaded / progressEvent.total) * 100);
 6         setProgress(progress);
 7       }
 8     })
 9       .then(response => {
10         // 创建一个临时的URL对象用于下载
11         const url = window.URL.createObjectURL(new Blob([response.data]));
12         const link = document.createElement('a');
13         link.href = url;
14         link.setAttribute('download', 'file.txt');
15         document.body.appendChild(link);
16         link.click();
17         document.body.removeChild(link);
18       })
19       .catch(error => {
20         console.error('文件下载失败:', error);
21       });
22   }
23   
24   
25   <button onClick={handleFileDownload}>下载文件</button>
26   <div>进度:{progress}%</div>

3. 大文件上传下载

3.1 大文件切片上传

- 使用 JavaScript 的 `File API` 获取文件对象,并使用 `Blob.prototype.slice()` 方法将文件切割为多个切片。

  • 使用 FormData 对象将切片数据通过 AJAX 或 Fetch API 发送到服务器。
  • 在后端服务器上接收切片并保存到临时存储中,等待后续合并。
  • 在客户端通过监听上传进度事件,在进度条或提示中展示上传进度。 代码示例
 1 const [file, setFile] = useState(null);  //用来存放我本地上传的文件
 2 
 3 const chunkSize = 1024 * 1024; // 1MB 切片大小
 4 
 5   const upload = () => {
 6     if (!file) {
 7       alert("请选择要上传的文件!");
 8       return;
 9     }
10 
11     const chunkSize = 1024 * 1024; // 1MB
12 
13     let start = 0;
14     let end = Math.min(chunkSize, file.size);
15 
16     while (start < file.size) {
17       const chunk = file.slice(start, end);
18       
19       // 创建FormData对象
20       const formData = new FormData();
21       formData.append('file', chunk);
22 
23       // 发送切片到服务器
24       fetch('上传接口xxxx', {
25         method: 'POST',
26         body: formData
27       })
28       .then(response => response.json())
29       .then(data => {
30         console.log(data);
31         // 处理响应结果
32       })
33       .catch(error => {
34         console.error(error);
35         // 处理错误
36       });
37 
38       start = end;
39       end = Math.min(start + chunkSize, file.size);
40     }
41   };
42   
43  return (
44     <div>
45       <input type="file" onChange={handleFileChange} />
46       <button onClick={upload}>上传</button>
47     </div>
48   );
49 }

在上面的代码中,创建了一个名为Upload的函数组件。它使用了 React 的useState钩子来管理选中的文件。

通过onChange事件监听文件输入框的变化,并在handleFileChange函数中获取选择的文件,并更新file状态。

点击“上传”按钮时,调用upload函数。它与之前的示例代码类似,将文件切割为多个大小相等的切片,并使用FormData对象和fetch函数发送切片数据到服务器。

3.2 实现断点续传的技术:记录和恢复上传状态

  • 在前端,可以使用 localStorage 或 sessionStorage 来存储已上传的切片信息,包括已上传的切片索引、切片大小等。
  • 每次上传前,先检查本地存储中是否存在已上传的切片信息,若存在,则从断点处继续上传。
  • 在后端,可以使用一个临时文件夹或数据库来记录已接收到的切片信息,包括已上传的切片索引、切片大小等。
  • 在上传完成前,保存上传状态,以便在上传中断后能够恢复上传进度
 1 import React, { useState, useRef, useEffect } from 'react';
 2 
 3 function Upload() {
 4   const [file, setFile] = useState(null);
 5   const [uploadedChunks, setUploadedChunks] = useState([]);
 6   const [uploading, setUploading] = useState(false);
 7   const uploadRequestRef = useRef();
 8 
 9   const handleFileChange = (event) => {
10     const selectedFile = event.target.files[0];
11     setFile(selectedFile);
12   };
13 
14   const uploadChunk = (chunk) => {
15     // 创建FormData对象
16     const formData = new FormData();
17     formData.append('file', chunk);
18 
19     // 发送切片到服务器
20     return fetch('your-upload-url', {
21       method: 'POST',
22       body: formData
23     })
24     .then(response => response.json())
25     .then(data => {
26       console.log(data);
27       // 处理响应结果
28       return data;
29     });
30   };
31 
32   const upload = async () => {
33     if (!file) {
34       alert("请选择要上传的文件!");
35       return;
36     }
37 
38     const chunkSize = 1024 * 1024; // 1MB
39     const totalChunks = Math.ceil(file.size / chunkSize);
40 
41     let start = 0;
42     let end = Math.min(chunkSize, file.size);
43 
44     setUploading(true);
45 
46     for (let i = 0; i < totalChunks; i++) {
47       const chunk = file.slice(start, end);
48       const uploadedChunkIndex = uploadedChunks.indexOf(i);
49 
50       if (uploadedChunkIndex === -1) {
51         try {
52           const response = await uploadChunk(chunk);
53           setUploadedChunks((prevChunks) => [...prevChunks, i]);
54 
55           // 保存已上传的切片信息到本地存储
56           localStorage.setItem('uploadedChunks', JSON.stringify(uploadedChunks));
57         } catch (error) {
58           console.error(error);
59           // 处理错误
60         }
61       }
62 
63       start = end;
64       end = Math.min(start + chunkSize, file.size);
65     }
66 
67     setUploading(false);
68 
69     // 上传完毕,清除本地存储的切片信息
70     localStorage.removeItem('uploadedChunks');
71   };
72 
73   useEffect(() => {
74     const storedUploadedChunks = localStorage.getItem('uploadedChunks');
75 
76     if (storedUploadedChunks) {
77       setUploadedChunks(JSON.parse(storedUploadedChunks));
78     }
79   }, []);
80 
81   return (
82     <div>
83       <input type="file" onChange={handleFileChange} />
84       <button onClick={upload} disabled={uploading}>
85         {uploading ? '上传中...' : '上传'}
86       </button>
87     </div>
88   );
89 }

首先,使用useState钩子创建了一个uploadedChunks状态来保存已上传的切片索引数组。初始值为空数组。

然后,我们使用useRef钩子创建了一个uploadRequestRef引用,用于存储当前的上传请求。

handleFileChange函数中,我们更新了file状态以选择要上传的文件。

uploadChunk函数中,我们发送切片到服务器,并返回一个Promise对象来处理响应结果。

upload函数中,我们添加了断点续传的逻辑。首先,我们获取切片的总数,并设置uploading状态为true来禁用上传按钮。

然后,我们使用for循环遍历所有切片。对于每个切片,我们检查uploadedChunks数组中是否已经包含该索引,如果不包含,则进行上传操作。

在上传切片之后,我们将已上传的切片索引添加到uploadedChunks数组,并使用localStorage保存已上传的切片信息。

最后,在上传完毕后,我们将uploading状态设为false,并清除本地存储的切片信息。

本文参考掘金:https://juejin.cn/post/7255189826226602045

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

并发编程的故事——共享模式之无锁

共享模式之无锁 文章目录 共享模式之无锁一、提出问题二、CAS和volatile三、原子整数四、原子引用五、原子数组六、原子更新器七、原子累加器八、unsafe 一、提出问题 关于对共享变量修改的多线程问题其实就是指令交错问题导致取值的时机相同&#xff0c;最后修改之后以最后一…

【基于空间纹理的残差网络无监督Pansharpening】

Unsupervised Pansharpening method Using Residual Network with Spatial Texture Attention &#xff08;基于空间纹理的残差网络无监督泛锐化方法&#xff09; 近年来&#xff0c;深度学习已经成为最受欢迎的泛锐化工具之一&#xff0c;许多相关方法已经被研究并反映出良好…

Qt xml解析之QXmlStreamReader

文章目录 背景QXmlStreamReader简单介绍使用QXmlStreamReader添加头文件<QXmlStreamReader>toString()toInt()完整代码 背景 项目中遇到需要解析某个方法返回的xml字符串&#xff0c;奈何C/C中没有原生的方法可供调用&#xff0c;只能使用第三方库&#xff0c;搜了一圈资…

OpenCV之filter2D函数

函数原型&#xff1a; CV_EXPORTS_W void filter2D(InputArray src,OutputArray dst,int ddepth,InputArray kernel,Point anchorPoint(-1,-1),double delta0,int borderTypeBORDER_DEFAULT); src: 原图像&#xff1b; dst&#xff1a;输出图像 &#xff0c;和输入的图像具有…

优秀的ui设计作品(合集)

UI设计师需要了解的九个Tips 1.图片类APP排版突破 规则是死的&#xff0c;人是活的。很多时候&#xff0c;如果需求是比较宽要尝试突破原则&#xff0c;用一些另类的排版方式&#xff0c;其实也是做好设计的本质。在图片类app中&#xff0c;错落一些的排版会使你的作品更有魅力…

Ubuntu系统安装VirtualBox后无发安装“增强功能”的解决办法

Ubuntu系统安装VirtualBox后&#xff0c; 在设备选项中点击曾倩功能没有反应&#xff0c;也不能是用多动和剪切板功能&#xff0c;解决办法如下 1. 安装linux-image Terminal执行: sudo apt-get install --reinstall linux-image-$(uname -r) 2. 安装virtualbox-guest-x11 …

9.Redis-zset

zset zset 有序集合 -> 升序常用命令zaddzcardzcountzrangezrevrange -> reverse 逆序zrangebyscorezpopmaxzpopminbzpopmax / bzpopminzrankzrevrankzscorezremzremrangebyrankzremrangebyscorezincrby集合间操作zinter -> 交集zunion -> 并集zdiff -> 差集zin…

0202hdfs的shell操作-hadoop-大数据学习

文章目录 1 进程启停管理2 文件系统操作命令2.1 HDFS文件系统基本信息2.2 介绍2.3 创建文件夹2.4 查看指定文件夹下的内容2.5 上传文件到HDFS2.6 查看HDFS文件内容2.7 下载HDFS文件2.8 HDFS数据删除操作 3 HDFS客户端-jetbrians产品插件3.1 Big Data Tools 安装3.2 配置windows…

睿趣科技:开抖音小店挣钱吗到底

在当今数字化时代&#xff0c;社交媒体平台成为了创业者们寻找商机和赚钱的新途径。而抖音作为一款风靡全球的短视频分享平台&#xff0c;自然也成为了许多人开设小店、进行创业的选择之一。那么&#xff0c;开抖音小店能否真正实现盈利&#xff0c;成为了一个备受关注的话题。…

并发编程的故事——共享模型之内存

共享模型之内存 文章目录 共享模型之内存一、JVM内存抽象模型二、可见性三、指令重排序 一、JVM内存抽象模型 主要就是把cpu下面的缓存、内存、磁盘等抽象成主存和工作内存 体现在 可见性 原子性 有序性 二、可见性 出现的问题 t线程如果频繁读取一个静态变量&#xff0c;那…

Android安卓实战项目(12)—关于身体分析,BMI计算,喝水提醒,食物卡路里计算APP【支持中英文切换】生活助手类APP(源码在文末)

Android安卓实战项目&#xff08;12&#xff09;—关于身体分析&#xff0c;BMI计算&#xff0c;喝水提醒&#xff0c;食物卡路里计算APP【支持中英文切换】生活助手类APP&#xff08;源码在文末&#x1f415;&#x1f415;&#x1f415;&#xff09; 一.项目运行介绍 B站演示…

第8章 字符输入/输出和输入验证

本章介绍以下内容&#xff1a; 更详细地介绍输入、输出以及缓冲输入和无缓冲输入的区别 如何通过键盘模拟文件结尾条件 如何使用重定向把程序和文件相连接 创建更友好的用户界面 在涉及计算机的话题时&#xff0c;我们经常会提到输入&#xff08;input&#xff09;和输出&#…

Facebook登录SDK

一、Facebook SDK接入 官方文档&#xff1a;https://developers.facebook.com/docs/facebook-login/android 按照流程填写完成 1、选择新建应用 如果已经创建了应用就点【搜索你的应用】&#xff0c;忽略2、3步骤 2、选择【允许用户用自己的Facebook账户登录】 3、填写应用…

简析vue文件编译——AST

简介 首先了解一个概念AST&#xff08;abstract syntax tree&#xff09;抽象语法树&#xff0c;按照大多数教程中的描述&#xff0c;这是一种源代码的抽象语法结构树&#xff0c;树上的每个节点都表示源代码中的一种结构&#xff0c;将源码中的各种嵌套括号等形式&#xff0c…

Android安卓实战项目(13)---记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用!!!(源码在文末)

Android安卓实战项目&#xff08;13&#xff09;—记账APP详细记录每天的收入和支出并且分类统计【生活助手类APP】强烈推荐自己也在用&#xff01;&#xff01;&#xff01;&#xff08;源码在文末&#x1f415;&#x1f415;&#x1f415;&#xff09; 一.项目运行介绍 B站…

antd实现年日输入框联动

效果: 1、默认显示年&#xff0c;日期区间默认显示今年2023——2024 年份显示前5年后5年 2、如果选择了月份&#xff0c;日期区间显示从1月份到12月份 部分代码: (react 使用class类组件)

Lee滤波python实现(还包括frost等滤波)

Lee滤波按定义实现&#xff1a; from scipy.ndimage.filters import uniform_filter from scipy.ndimage.measurements import variancedef lee_filter(img, size):img_mean uniform_filter(img, (size, size))img_sqr_mean uniform_filter(img**2, (size, size))img_varian…

最小生成树 -prim算法

一般无向图建图稠密图-prim算法稀疏图-kruskal算法 prim : 加点法 1.先随机选一个点&#xff0c;加入集合 &#xff0c;之后寻找最短的距离的点加入集合&#xff0c;行程最小生成树。 2.注意最小生成树是不能有回路的&#xff0c; 所以可以把回路设置成最大值&#xff0c;即假装…

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决

idea使用maven时的java.lang.IllegalArgumentException: Malformed \uxxxx encoding问题解决 欢迎使用Markdown编辑器1、使用maven clean install -X会提示报错日志2、在Poperties.java文件的这一行打上断点3、maven debug进行调试4、运行到断点位置后&#xff0c;查看报错char…

贝锐蒲公英异地组网方案,如何阻断网络安全威胁?

随着混合云和移动办公的普及&#xff0c;企业网络面临着越来越复杂的安全威胁环境。 大型企业有足够的能力和预算&#xff0c;构建覆盖全部个性化需求的定制化网络安全方案。 但对于广大中小企业来说&#xff0c;由于实际业务发展情况&#xff0c;他们难以在部署周期、预算成本…