1/* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16import JSZip from 'jszip'; 17import {FunctionUtils, OnProgressUpdateType} from './function_utils'; 18 19export type OnFile = (file: File, parentArchive: File | undefined) => void; 20 21class FileUtils { 22 static getFileExtension(file: File) { 23 const split = file.name.split('.'); 24 if (split.length > 1) { 25 return split.pop(); 26 } 27 return undefined; 28 } 29 30 static removeDirFromFileName(name: string) { 31 if (name.includes('/')) { 32 const startIndex = name.lastIndexOf('/') + 1; 33 return name.slice(startIndex); 34 } else { 35 return name; 36 } 37 } 38 39 static async createZipArchive(files: File[]): Promise<Blob> { 40 const zip = new JSZip(); 41 for (let i = 0; i < files.length; i++) { 42 const file = files[i]; 43 const blob = await file.arrayBuffer(); 44 zip.file(file.name, blob); 45 } 46 return await zip.generateAsync({type: 'blob'}); 47 } 48 49 static async unzipFile( 50 file: File, 51 onProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING 52 ): Promise<File[]> { 53 const unzippedFiles: File[] = []; 54 const zip = new JSZip(); 55 const content = await zip.loadAsync(file); 56 57 const filenames = Object.keys(content.files); 58 for (const [index, filename] of filenames.entries()) { 59 const file = content.files[filename]; 60 if (file.dir) { 61 // Ignore directories 62 continue; 63 } else { 64 const fileBlob = await file.async('blob'); 65 const unzippedFile = new File([fileBlob], filename); 66 unzippedFiles.push(unzippedFile); 67 } 68 69 onProgressUpdate((100 * (index + 1)) / filenames.length); 70 } 71 72 return unzippedFiles; 73 } 74 75 static async unzipFilesIfNeeded( 76 files: File[], 77 onFile: OnFile, 78 onProgressUpdate: OnProgressUpdateType = FunctionUtils.DO_NOTHING 79 ) { 80 for (let i = 0; i < files.length; i++) { 81 const file = files[i]; 82 83 const onSubprogressUpdate = (subPercentage: number) => { 84 const percentage = (100 * i) / files.length + subPercentage / files.length; 85 onProgressUpdate(percentage); 86 }; 87 88 if (FileUtils.isZipFile(file)) { 89 const unzippedFile = await FileUtils.unzipFile(file, onSubprogressUpdate); 90 unzippedFile.forEach((unzippedFile) => onFile(unzippedFile, file)); 91 } else { 92 onFile(file, undefined); 93 } 94 } 95 } 96 97 static isZipFile(file: File) { 98 return FileUtils.getFileExtension(file) === 'zip'; 99 } 100} 101 102export {FileUtils}; 103