文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

浏览器中处理上传大文件——FileReader.readAsArrayBuffer()方法

2023-09-03 16:53

关注

1.问题点

平常从客户端上传文件到服务器端,只需要读取 input 的 File 对象,然后将其塞到 FormData 对象中,然后使用 ajax 发送到服务器端,服务器端会有配套的读文件和写文件的操作。

现在遇到一个问题,就是在本地一个进程中,浏览器和其他代码间数据的发送,因为不经过 HTTP,这时候只有将文件的信息读取出来,然后通过二进制或是 base64 编码的格式发送。

HTML5 的 FileReader API 可以在浏览器对用户本地文件进行读取,但是在使用的过程中又遇到了新的问题,比如通过 FileReader.readAsDataURL() ,使用时它会读取一整个文件的信息,加载到内存中,50MB 以内的文件基本不会遇到什么问题,但是当文件达到 300MB 甚至是 1G 以上,直接读取一整个文件,会导致内存超出上限,浏览器这时候就会崩溃,因此如果想要处理大的文件,我们只能将大文件切成一块一块的,将其变成小文件后再去处理,FileReader 也提供了相关的 API,即 FileReader.readAsArrayBuffer() ,下面就看一下使用 FileReader.readAsArrayBuffer() 进行切块的处理吧。

2.大文件切块处理的思路

  1. 使用 FileReader.readAsArrayBuffer() 读取文件,在读取成功后 result 属性中将包含一个 ArrayBuffer 对象以表示所读取文件的数据。
  2. 对读取到的 ArrayBuffer 对象进行拆分,放到一个数组中
  3. 使用 Promise.all 来顺序的处理拆分后的数组(保证顺序传输)
  4. 使用 Uint8Array 将 ArrayBuffer 拆后后的数组生成 二进制字节数组
  5. 最后将二进制字节数组转为 base64 编码的字符串
  6. 传输的是 base64 编码的字符串,到达目的地后,需要使用 base64 解码,才能得到原始的二进制字节信息
FileReader提供的方法说明
readAsArrayBuffer(file)按字节读取文件内容,结果用ArrayBuffer对象表示
readAsBinaryString(file)按字节读取文件内容,结果为文件的二进制串
readAsDataURL(file)读取文件内容,结果用data:url的字符串形式表示
readAsText(file,encoding)按字符读取文件内容,结果用字符串形式表示
abort()终止文件读取操作
FileReader事件说明
onloadstart当读取操作开始时调用
当读取操作开始时调用在读取数据过程中周期性调用
onabort当读取操作被中止时调用
onerror当读取操作发生错误时调用
onload当读取操作成功完成时调用
onloadend当读取操作完成时调用,无论成功,失败或取消

3.以下代码实现的功能

DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Documenttitle>    <style>        * {            padding: 0;            margin: 0;        }        body {            width: 100vw;            height: 100vh;        }        .drag-area-container {            width: 800px;            height: 800px;            border: 1px solid #0f0;        }        .highlight {            border: 10px solid #0f0;        }    style>head><body>    <div class="drag-area-container">    div>    <script>        (function() {            // 禁止浏览器的拖拽默认事件            var globalDragFile = function() {                let globalDragArea = document.querySelector('body')                globalDragArea.addEventListener('dragover',function(e){                    e.preventDefault()                })                globalDragArea.addEventListener('drop', function(e) {                    e.preventDefault()                })            }            // 识别指定区域的拖动效果            var localDragFile = function() {                let localDragArea = document.querySelector('.drag-area-container')                localDragArea.addEventListener('dragenter',function(e){                    // 拖动文件到指定区域时,添加高亮                    localDragArea.classList.add('highlight')                    e.preventDefault()                })                localDragArea.addEventListener('dragover',function(e){                    e.preventDefault()                })                localDragArea.addEventListener('drop', function(e) {                    e.preventDefault()                    // 去除高亮                    localDragArea.classList.remove('highlight')                    var file = e.dataTransfer.files[0]                    console.log("file = ", file);                    uploadFile(file)                })            }            // 处理文件 && 上传文件            var uploadFile = function(file) {                // 使用 FileReader 读取文件的数据                var reader = new FileReader()                reader.onloadend = function() {                    var file_result = this.result               // ArrayBuffer 数据对象                    var file_length = file_result.byteLength                    // 小于 20 MB 为小文件,则整个读取并上传                    // 大于 20 MB 为大文件,则需要将它切成小块,分别上传                    var step = 1024 * 1024 * 20                    if (file_length < step) {                        console.log("小文件,直接整个上传 ");                        handleSmallFile(file_result)                    } else {                        console.log("大文件,切块分别上传 ");                        var block_arr = splitBigFile(file_result, file_length, step)                        handleBigFile(block_arr).then(function(results) {console.log("大文件,切块上传成功 result = ", results)                        })                    }                }                reader.readAsArrayBuffer(file)                            }            var handleSmallFile = function(file_result) {                // 先读取到 ArrayBuffer,再获取 ArrayBuffer 的 Uint8Array 字节数组形式,最后用 base64 编码字节数组用于传输。                var unit8_data = new Uint8Array(file_result)            // 提取二进制字节数组,使用 Uint8Array 表示                var base64_data = binary2base64(unit8_data)             // base64 编码                console.log("==== handle upload start ====");                console.log("data = ", base64_data);                console.log("==== handle upload end ====");            }            // 根据指定的 step 大小,切出来指定的 step 大小的块            var splitBigFile = function(file, file_length, step) {                var step_times = Math.ceil(file_length / step)                var start = 0                var block_arr = []                for (i = 0; i < step_times; i++) {                    var block = file.slice(start, start + step)                    start = start + step                    block_arr.push(block)                }                return block_arr            }            var handleBigFile = async function(big_files) {                return Promise.all([].map.call(big_files, function(file, index) {                    return new Promise(function(resolve, reject) {                        // 先读取到 ArrayBuffer,再获取 ArrayBuffer 的 Uint8Array 字节数组形式,最后用 base64 编码字节数组用于传输。                        var view = new Uint8Array(file)             // 提取二进制字节数组,使用 Uint8Array 表示                        var base64_data = binary2base64(view)       // base64 编码                        console.log("==== handle upload start ====");                        console.log("block index = ", index);                        console.log("data = ", base64_data);                        console.log("==== handle upload end ====");                        resolve("Promise file")                    })                })).then(function(results) {                    return results;                })            }            // 二进制字节数组转 base64 编码的字符串            var binary2base64 = function(bi) {                let str = '';                for (let i = 0, len = bi.length; i < len; i++) {                    str += String.fromCharCode(bi[i]);                }                return btoa(str);            }            var __main = function() {                // 禁止浏览器的拖拽默认事件                globalDragFile()                // 识别指定区域的拖动效果                localDragFile()            }            __main()        })()    script>body>html>

来源地址:https://blog.csdn.net/weixin_44860226/article/details/129797332

阅读原文内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-服务器
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯