Isuru
¿Hay alguna manera de comprimir archivos usando JavaScript? Por ejemplo, como en el correo de Yahoo, cuando elige descargar todos los archivos adjuntos de un correo electrónico, se comprime y descarga en un solo archivo zip. ¿Es JavaScript capaz de hacer eso? Si es así, proporcione un ejemplo de codificación.
Encontré esta biblioteca llamada jszip para hacer la tarea, pero tiene problemas conocidos y no resueltos.
¿Cómo resuelvo el problema?
Yuci
JSZip se ha actualizado a lo largo de los años. Ahora puedes encontrarlo en su repositorio de GitHub
Se puede usar junto con FileSaver.js
Puede instalarlos usando npm:
npm install jszip --save
npm install file-saver --save
Y luego importarlos y usarlos:
import JSZip from 'jszip';
import FileSaver from 'file-saver';
const zip = new JSZip();
zip.file('idlist.txt', 'PMID:29651880\r\nPMID:29303721');
zip.generateAsync({ type: 'blob' }).then(function (content) {
FileSaver.saveAs(content, 'download.zip');
});
Luego, descargará un archivo zip llamado download.zip, una vez que lo haya extraído, y puede encontrar dentro un archivo llamado idlist.txt, que tiene dos líneas:
PMID:29651880
PMID:29303721
Y para su referencia, probé con los siguientes navegadores y todos pasaron:
- Firefox 59.0.2 (Windows 10)
- Chrome 65.0.3325.181 (Windows 10)
- Microsoft Edge 41.16299.371.0 (Windows 10)
- Internet Explorer 11.0.60 (Windows 10)
- Ópera 52 (Mac OSX 10.13)
- Safari 11 (Mac OSX 10.13)
-
Esto no funciona para mí. Entiendo esto:
UnhandledPromiseRejectionWarning: Error: blob is not supported by this platform
Además, ¿cómo puedo comprimir un archivo ya existente en el mismo directorio?– júpiterjelly
21 de julio de 2021 a las 20:41
Si no te importa IE, cliente-zip es mucho más rápido y más pequeño que JSZip y está destinado a resolver exactamente este problema (sí, este es un complemento desvergonzado pero completamente relevante para mi biblioteca 😉).
Usted haría algo como esto (donde files
podría ser una matriz de fetch
Respuestas, por ejemplo, aunque hay muchas entradas admitidas):
import { downloadZip } from "client-zip/index.js"
import FileSaver from "file-saver"
const content = await downloadZip(files).blob()
FileSaver.saveAs(content, "download.zip");
Esencialmente el mismo uso que en la respuesta de Yuci pero actualizado para 2020.
-
Pero client-zip no comprime los datos
– Gon Vagner
30 de diciembre de 2020 a las 17:36
-
Eso no es necesariamente algo malo. De hecho, muchos navegadores descomprimen automáticamente los archivos descargados, por lo que la compresión solo aumentaría la carga de la CPU (para la compresión y luego nuevamente para la descompresión) sin ahorrar ancho de banda ni almacenamiento. La principal razón para comprimir aquí parece ser lograr una descarga de varios archivos adjuntos con un solo clic.
– Touffy
31 de diciembre de 2020 a las 13:58
-
No dije que eso es algo malo. Pero mencionó ‘exactamente este problema’ que puede parecer que debería hacer lo mismo que JSZip. Client-zip parece increíble para hacer lo que propone.
– Gon Vagner
31 de diciembre de 2020 a las 18:02
-
Todo bien. Para ser claros: no afirmo que client-zip resuelva el mismo grupo de problemas que JSZip, solo el de esta pregunta.
– Touffy
1 de enero de 2021 a las 20:57
-
el zip del cliente actualizado parece aún mucho más fácil sin la necesidad de un protector de archivos
–Nick Chan Abdullah
21 de junio de 2021 a las 3:32
Sangeetharaj
Al usar JSZIP, podemos generar y descargar un archivo zip en JavaScript. Para eso tienes que seguir los siguientes pasos
- Descargue el archivo zip jszip de http://github.com/Stuk/jszip/zipball/maestro
- Extraiga el zip y busque el archivo jszip.js dentro de la carpeta dist
-
Importe el archivo jszip.js en su archivo html como se muestra a continuación
<script type="text/javascript" src="https://stackoverflow.com/questions/8608724/jszip.js"></script>
-
Agregue la siguiente función en su código y llámela
onClickDownload: function () { var zip = new JSZip(); for (var i = 0; i < 5; i++) { var txt="hello"; zip.file("file" + i + ".txt", txt); } zip.generateAsync({ type: "base64" }).then(function(content) { window.location.href = "https://stackoverflow.com/questions/8608724/data:application/zip;base64," + content; }); }
-
Puede descargar código de muestra de mi repositorio git aquí (enlace GIT)
-
Es una idea bastante mala generar la salida en Base64, e incluso una idea peor usar un URI de datos como ese. Un desperdicio de memoria y CPU. Utilice una URL de blob en su lugar.
– brad
23 de septiembre de 2019 a las 3:08
-
¡Excelente, cambié el tipo a ‘blob’ y funcionó de maravilla! Usé un enlace de anclaje en la página para activar la descarga en lugar de cambiar la URL de la página. var uriContent = URL.createObjectURL(contentBlob); var lnkDownload = document.getElementById(‘lnkDownload’); lnkDownload.download = ‘MiDescarga.zip’; lnkDownload.href = uriContent; lnkDescargar.click();
– tgraupmann
13 mayo 2020 a las 21:57
Desarrollé una nueva solución para generar archivos zip usando JavaScript. La solución es de dominio público.
La clase Zip.
Zip {
constructor(name) {
this.name = name;
this.zip = new Array();
this.file = new Array();
}
dec2bin=(dec,size)=>dec.toString(2).padStart(size,'0');
str2dec=str=>Array.from(new TextEncoder().encode(str));
str2hex=str=>[...new TextEncoder().encode(str)].map(x=>x.toString(16).padStart(2,'0'));
hex2buf=hex=>new Uint8Array(hex.split(' ').map(x=>parseInt(x,16)));
bin2hex=bin=>(parseInt(bin.slice(8),2).toString(16).padStart(2,'0')+' '+parseInt(bin.slice(0,8),2).toString(16).padStart(2,'0'));
reverse=hex=>{
let hexArray=new Array();
for(let i=0;i<hex.length;i=i+2)hexArray[i]=hex[i]+''+hex[i+1];
return hexArray.filter((a)=>a).reverse().join(' ');
}
crc32=r=>{
for(var a,o=[],c=0;c<256;c++){
a=c;
for(var f=0;f<8;f++)a=1&a?3988292384^a>>>1:a>>>1;
o[c]=a;
}
for(var n=-1,t=0;t<r.length;t++)n=n>>>8^o[255&(n^r
return this.reverse(((-1^n)>>>0).toString(16).padStart(8,'0'));
}
fecth2zip(filesArray,folder=""){
filesArray.forEach(fileUrl=>{
let resp;
fetch(fileUrl).then(response=>{
resp=response;
return response.arrayBuffer();
}).then(blob=>{
new Response(blob).arrayBuffer().then(buffer=>{
console.log(`File: ${fileUrl} load`);
let uint=[...new Uint8Array(buffer)];
uint.modTime=resp.headers.get('Last-Modified');
uint.fileUrl=`${this.name}/${folder}${fileUrl}`;
this.zip[fileUrl]=uint;
});
});
});
}
str2zip(name,str,folder){
let uint=[...new Uint8Array(this.str2dec(str))];
uint.name=name;
uint.modTime=new Date();
uint.fileUrl=`${this.name}/${folder}${name}`;
this.zip[uint.fileUrl]=uint;
}
files2zip(files,folder){
for(let i=0;i<files.length;i++){
files[i].arrayBuffer().then(data=>{
let uint=[...new Uint8Array(data)];
uint.name=files[i].name;
uint.modTime=files[i].lastModifiedDate;
uint.fileUrl=`${this.name}/${folder}${files[i].name}`;
this.zip[uint.fileUrl]=uint;
});
}
}
makeZip(){
let count=0;
let fileHeader="";
let centralDirectoryFileHeader="";
let directoryInit=0;
let offSetLocalHeader="00 00 00 00";
let zip=this.zip;
for(const name in zip){
let modTime=()=>{
lastMod=new Date(zip[name].modTime);
hour=this.dec2bin(lastMod.getHours(),5);
minutes=this.dec2bin(lastMod.getMinutes(),6);
seconds=this.dec2bin(Math.round(lastMod.getSeconds()/2),5);
year=this.dec2bin(lastMod.getFullYear()-1980,7);
month=this.dec2bin(lastMod.getMonth()+1,4);
day=this.dec2bin(lastMod.getDate(),5);
return this.bin2hex(`${hour}${minutes}${seconds}`)+' '+this.bin2hex(`${year}${month}${day}`);
}
let crc=this.crc32(zip[name]);
let size=this.reverse(parseInt(zip[name].length).toString(16).padStart(8,'0'));
let nameFile=this.str2hex(zip[name].fileUrl).join(' ');
let nameSize=this.reverse(zip[name].fileUrl.length.toString(16).padStart(4,'0'));
let fileHeader=`50 4B 03 04 14 00 00 00 00 00 ${modTime} ${crc} ${size} ${size} ${nameSize} 00 00 ${nameFile}`;
let fileHeaderBuffer=this.hex2buf(fileHeader);
directoryInit=directoryInit+fileHeaderBuffer.length+zip[name].length;
centralDirectoryFileHeader=`${centralDirectoryFileHeader}50 4B 01 02 14 00 14 00 00 00 00 00 ${modTime} ${crc} ${size} ${size} ${nameSize} 00 00 00 00 00 00 01 00 20 00 00 00 ${offSetLocalHeader} ${nameFile} `;
offSetLocalHeader=this.reverse(directoryInit.toString(16).padStart(8,'0'));
this.file.push(fileHeaderBuffer,new Uint8Array(zip[name]));
count++;
}
centralDirectoryFileHeader=centralDirectoryFileHeader.trim();
let entries=this.reverse(count.toString(16).padStart(4,'0'));
let dirSize=this.reverse(centralDirectoryFileHeader.split(' ').length.toString(16).padStart(8,'0'));
let dirInit=this.reverse(directoryInit.toString(16).padStart(8,'0'));
let centralDirectory=`50 4b 05 06 00 00 00 00 ${entries} ${entries} ${dirSize} ${dirInit} 00 00`;
this.file.push(this.hex2buf(centralDirectoryFileHeader),this.hex2buf(centralDirectory));
let a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([...this.file],{type:'application/octet-stream'}));
a.download = `${this.name}.zip`;
a.click();
}
}
Luego, crea un nuevo objeto Zip.
z=new Zip('myZipFileName');
Tu puedes:
- Cargue archivos de su directorio para comprimir objetos con feth2zip (filesArray, carpeta).
filesArray=[
'file01.ext',
'file02.ext',
'file...'
];
z.fecth2zip(filesArray,'public/');
- Cree un nuevo archivo a partir de una cadena con str2zip(nameFile,content,directory).
z.str2zip('test.txt','content','public/teste/');
-
O subir a zip.
Coloque el evento onchange en el archivo de entrada y envíe los archivos a la función files2zip(this.files).
<input type="file" onchange="z.files2zip(this.files)" value="files" multiple>
Después de colocar todos los objetos dentro de su archivo de objeto Zip, simplemente descárguelo.
<input type="button" onclick="z.makeZip()" value="Zip">
La clase también se puede encontrar aquí: https://github.com/pwasystem/zip/
A. Tran
Recomiendo ir directamente a usar del nodo biblioteca incorporada Zlib para ello, que incluye imágenes; codificar en base 64 usando “buffers”. En lugar de usar npm paquetes Las razones son:
- Zlib es una biblioteca nativa de Node – se ha mantenido actualizado durante casi 10 años, por lo que las pruebas allí para soportes a largo plazo
- Node te permite trabajar con Buffers – es decir, puede convertir su cadena de texto/imágenes en datos binarios sin procesar y comprimirlos de esa manera con Zlib
- Comprime y descomprime fácilmente archivos grandes – aproveche los flujos de nodos para comprimir archivos en MB o GB
El hecho de que esté usando jszip me permitiría adivinar que está usando npm y node; asume que ha configurado su entorno correctamente, es decir, nodo instalado globalmente.
Ejemplo: entrada.txt comprimido para convertirse entrada.txt.gz
const zlib = require('zlib');
const fs = require('fs');
const gzip = zlib.createGzip();
const input = fs.createReadStream('input.txt');
const output = fs.createWriteStream('input.txt.gz');
input.pipe(gzip).pipe(output);
Paso 1: Vos tambien require
cada uno de los módulos nativos de node – require es parte de ES5. Zlib como se mencionó anteriormente, y fs
módulo, el Sistema de archivos módulo.
const zlib = require('zlib');
const fs = require('fs');
Paso 2: Él fs módulo, esto le permite crear un flujo de lectura, se llaman específicamente para leer fragmentos de datos. Esto devolverá un flujo de lectura objeto; corriente legible
const input = fs.createReadStream(FILE PATH HERE);
__Nota: este objeto de flujo de lectura se canaliza nuevamente; este encadenamiento de tuberías en objetos readsteam puede ocurrir sin fin, lo que hace que las tuberías sean muy flexibles.
ReadStream.pipe(DoesSomething).pipe(SomethingElse).pipe(ConvertToWriteStream)
Paso 3: El objeto de flujo de lectura, que se ha canalizado y comprimido, se convierte luego en un objeto de flujo de escritura.
const output = fs.createWriteStream('input.txt.gz');
input.pipe(gzip).pipe(output); // returned filename input.txt.gz, within local directory
Entonces, esta biblioteca le permite ingresar fácilmente una ruta de archivo y decidir dónde desea que esté su archivo comprimido. También puede optar por hacer lo contrario, si es necesario.
-
Gracias por compartir. Sin embargo, supongo que la pregunta buscaba una solución del lado del navegador en lugar del lado del servidor. ¿Alguna idea en el lado del navegador? Gracias.
– Yuci
15 de abril de 2018 a las 7:44
Alex Turpin
Con las nuevas API de archivos HTML5 y las matrices escritas, puede hacer prácticamente todo lo que quiera en JavaScript. Sin embargo, el soporte del navegador no va a ser muy bueno. Supongo que eso es lo que quisiste decir con “problemas sin resolver”. Recomendaría, por el momento, hacerlo en el servidor. Por ejemplo, en PHP, podrías usar esta extensión.
-
Gracias por compartir. Sin embargo, supongo que la pregunta buscaba una solución del lado del navegador en lugar del lado del servidor. ¿Alguna idea en el lado del navegador? Gracias.
– Yuci
15 de abril de 2018 a las 7:44
Esta es una pregunta anterior, pero la encontré buscando una solución para crear un archivo zip.
Para mi caso de uso, estoy creando varios miles de archivos zip en node.js desde una fuente de registro muy grande cada minuto que consta de hasta 200 archivos en cada archivo.
Probé JSZip con muy malos resultados debido a problemas de rendimiento y una pérdida de memoria que no valía la pena rastrear. Dado que mi caso de uso es bastante extremo, es toda una prueba de estrés.
Encontré otra biblioteca zip de JavaScript puro que vale la pena mencionar aquí para que otros la vean.
terminé usando inflar.
https://github.com/101arrowz/flate
Esta biblioteca ha sido extremadamente eficaz. Solo estoy usando la función de archivo zip con compresión de nivel 9, pero fflate es una biblioteca con todas las funciones que funciona en el lado del servidor y es compatible con el navegador completo (2011+). No incluyo ningún ejemplo aquí ya que la documentación plana es muy completa. Lo recomiendo ampliamente como alternativa.
-
Gracias por compartir, esta biblioteca plana se ve muy limpia.
– Ginebra Quin
22 de enero a las 12:19
posible duplicado de stackoverflow.com/questions/2095697/…
– kvc
22 de diciembre de 2011 a las 19:23
@kvc También vi eso, pero mi pregunta es para comprimir archivos. No descomprimir. Así que pensé en preguntarlo como otra publicación 🙂
– Isuru
22 de diciembre de 2011 a las 19:28
Es posible que pueda crear un javascript de shell de Windows que haga esto, pero no puede hacerlo en el navegador.
– kvc
22 de diciembre de 2011 a las 19:35