如何用JavaScript解压Base64格式的Zip文件及问题排查
从Base64编码的Zip文件提取内容的问题
问题1:fflate库使用报错
我有一段对应Zip文件的Base64字符串,需要提取其中的文件。最初使用fflate库尝试,代码如下:
attachmentsFlat.forEach(async (attachment) => { attachment = await emailStore.getAttachment(props.mailbox, attachment.email_id, attachment.id) if(attachment.file_name.split('.').pop() == 'zip') { const decompressed = fflate.decompressSync(attachment.content_bytes); console.log(decompressed) } })
运行后出现错误:
fflate.js?v=2017d673:285 Uncaught (in promise) TypeError: dat.subarray is not a function at inflt (fflate.js?v=2017d673:285:21) at inflateSync (fflate.js?v=2017d673:1103:10) at Module.decompressSync (fflate.js?v=2017d673:1421:160) at EmailFilesAssignment.vue:150:37
问题2:JSZip异步获取文件失败
改用JSZip后,无法正确异步获取文件,代码如下:
attachmentsFlat.forEach(async (attachment) => { attachment = await emailStore.getAttachment(props.mailbox, attachment.email_id, attachment.id) if(attachment.file_name.split('.').pop() == 'zip') { const zipFiles = await uncompressZipAttachment(attachment) console.log(zipFiles) // 控制台显示有8条条目但数组为空 console.log(zipFiles[0]) // 始终返回undefined } }) const dataURLtoFile = (base64String: string, contentType: string | undefined, filename: string) => { let mime = contentType let bstr = atob(base64String) let n = bstr.length let u8arr = new Uint8Array(n) while(n--){ u8arr[n] = bstr.charCodeAt(n); } return new File([u8arr], filename, {type:mime}); } const uncompressZipAttachment = async (attachment : FileAttachment) : Promise<any> => { let files : File[] = [] const decompressedZipFiles = await jsZip.loadAsync(dataURLtoFile(attachment.content_bytes, attachment.content_type, attachment.file_name)) decompressedZipFiles.forEach(async (filename) => { let fileData = await decompressedZipFiles.files[filename].async("blob"); files.push(new File([fileData], filename)) }) return files }
解决方案
修复fflate的使用问题
fflate的decompressSync需要接收Uint8Array类型的二进制数据,而非直接传入Base64字符串。先将Base64转为Uint8Array再传入:
attachmentsFlat.forEach(async (attachment) => { attachment = await emailStore.getAttachment(props.mailbox, attachment.email_id, attachment.id) if(attachment.file_name.split('.').pop() === 'zip') { // Base64转Uint8Array const bstr = atob(attachment.content_bytes); const u8arr = new Uint8Array(bstr.length); for (let i = 0; i < bstr.length; i++) { u8arr[i] = bstr.charCodeAt(i); } // 传入二进制数组解压 const decompressed = fflate.decompressSync(u8arr); console.log(decompressed); } })
修复JSZip的异步问题
forEach无法等待内部异步操作完成,导致函数提前返回空数组。改用Promise.all等待所有文件处理完成:
const uncompressZipAttachment = async (attachment: FileAttachment): Promise<File[]> => { // 直接将Base64转Uint8Array,无需转File对象,JSZip支持直接加载二进制数据 const bstr = atob(attachment.content_bytes); const u8arr = new Uint8Array(bstr.length); for (let i = 0; i < bstr.length; i++) { u8arr[i] = bstr.charCodeAt(i); } const zip = await JSZip.loadAsync(u8arr); // 收集所有文件的异步处理Promise,跳过目录 const filePromises = Object.keys(zip.files).map(async (filename) => { if (!zip.files[filename].dir) { const blob = await zip.files[filename].async("blob"); return new File([blob], filename); } }); // 过滤目录对应的undefined结果,等待所有操作完成 const files = (await Promise.all(filePromises)).filter(Boolean); return files; }
修改后zipFiles会正确返回解压后的文件数组,zipFiles[0]也能获取到对应文件。
内容的提问来源于stack exchange,提问作者Qiuzman
相关产品推荐
相关产品推荐

