Descomprimir archivos


79

Quiero mostrar archivos de OpenOffice , .odt y .odp en el lado del cliente usando un navegador web.

Estos archivos son archivos comprimidos. Usando Ajax, puedo obtener estos archivos del servidor, pero son archivos comprimidos. Tengo que descomprimirlos usando JavaScript , he intentado usar inflate.js, http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt , pero sin éxito.

¿Cómo puedo hacer esto?


7
"sin éxito", sea más específico, muéstrenos un código, muéstrenos algunos errores ... estamos aquí para ayudar, no para adivinar.
OcuS

Básicamente, acabo de llamar a inflate function - data = zip_inflate (src); Pero creo que esto es para un solo archivo. Si un archivo zip contiene varios archivos en una estructura de directorio, ¿cuál será el contenido de los "datos"? No sé cómo usar esta biblioteca.
user69260

@Eimantas ¿qué significa? + O -
user69260

4
@techfandu: (1) Haga clic en su nombre. (2) Haga clic en una pregunta anterior que haya hecho. (3) Acepte la respuesta que más le ayudó. (4) Repita hasta que todas sus preguntas tengan una respuesta aceptada.
Dave Jarvis

has tenido éxito con el trabajo? Tengo que hacer lo mismo para un proyecto en la escuela (jugar odp en un navegador web). Si me pudieran dar algunos consejos, sería increíble.
Alexx

Respuestas:


61

Escribí un descomprimidor en Javascript. Funciona.

Se basa en el lector de archivos binarios de Andy GP Na y en alguna lógica de inflado RFC1951 de notmasteryet . Agregué la clase ZipFile.

ejemplo de trabajo:
http://cheeso.members.winisp.net/Unzip-Example.htm (enlace muerto)

La fuente:
http://cheeso.members.winisp.net/srcview.aspx?dir=js-unzip (enlace muerto)

NB : los enlaces están muertos; Pronto encontraré un nuevo anfitrión.

En la fuente se incluye una página de demostración ZipFile.htm y 3 scripts distintos, uno para la clase zipfile, uno para la clase inflar y otro para una clase de lector de archivos binarios. La demostración también depende de jQuery y jQuery UI. Si acaba de descargar el archivo js-zip.zip, todas las fuentes necesarias están allí.


Así es como se ve el código de la aplicación en Javascript:

// In my demo, this gets attached to a click event.
// it instantiates a ZipFile, and provides a callback that is
// invoked when the zip is read.  This can take a few seconds on a
// large zip file, so it's asynchronous. 
var readFile = function(){
    $("#status").html("<br/>");
    var url= $("#urlToLoad").val();
    var doneReading = function(zip){
        extractEntries(zip);
    };

    var zipFile = new ZipFile(url, doneReading);
};


// this function extracts the entries from an instantiated zip
function extractEntries(zip){
    $('#report').accordion('destroy');

    // clear
    $("#report").html('');

    var extractCb = function(id) {
        // this callback is invoked with the entry name, and entry text
        // in my demo, the text is just injected into an accordion panel.
        return (function(entryName, entryText){
            var content = entryText.replace(new RegExp( "\\n", "g" ), "<br/>");
            $("#"+id).html(content);
            $("#status").append("extract cb, entry(" + entryName + ")  id(" + id + ")<br/>");
            $('#report').accordion('destroy');
            $('#report').accordion({collapsible:true, active:false});
        });
    }

    // for each entry in the zip, extract it. 
    for (var i=0; i<zip.entries.length;  i++) {
        var entry = zip.entries[i];

        var entryInfo = "<h4><a>" + entry.name + "</a></h4>\n<div>";

        // contrive an id for the entry, make it unique
        var randomId = "id-"+ Math.floor((Math.random() * 1000000000));

        entryInfo += "<span class='inputDiv'><h4>Content:</h4><span id='" + randomId +
            "'></span></span></div>\n";

        // insert the info for one entry as the last child within the report div
        $("#report").append(entryInfo);

        // extract asynchronously
        entry.extract(extractCb(randomId));
    }
}

La demostración funciona en un par de pasos: el readFilefn se activa con un clic y crea una instancia de un objeto ZipFile, que lee el archivo zip. Hay una devolución de llamada asincrónica para cuando se completa la lectura (generalmente ocurre en menos de un segundo para cremalleras de tamaño razonable); en esta demostración, la devolución de llamada se mantiene en la variable local doneReading, que simplemente llama extractEntries, que simplemente descomprime ciegamente todo el contenido de la archivo zip. En una aplicación real, probablemente elegiría algunas de las entradas para extraer (permitir que el usuario seleccione o elija una o más entradas mediante programación, etc.).

El extractEntriesfn itera sobre todas las entradas y llama extract()a cada una, pasando una devolución de llamada. La descompresión de una entrada lleva tiempo, tal vez 1 so más por cada entrada en el archivo zip, lo que significa que la asincronía es apropiada. La devolución de llamada de extracción simplemente agrega el contenido extraído a un acordeón jQuery en la página. Si el contenido es binario, se formatea como tal (no se muestra).


Funciona, pero creo que la utilidad es algo limitada.

Por un lado: es muy lento. Tarda ~ 4 segundos en descomprimir el archivo 140k AppNote.txt de PKWare. La misma descompresión se puede realizar en menos de .5 segundos en un programa .NET. EDITAR : El Javascript ZipFile se descomprime considerablemente más rápido que esto ahora, en IE9 y en Chrome. Sigue siendo más lento que un programa compilado, pero es bastante rápido para el uso normal del navegador.

Por otro: no hace streaming. Básicamente, absorbe todo el contenido del archivo zip en la memoria. En un entorno de programación "real", podría leer solo los metadatos de un archivo zip (por ejemplo, 64 bytes por entrada) y luego leer y descomprimir los demás datos como desee. No hay forma de hacer IO así en javascript, hasta donde yo sé, por lo tanto, la única opción es leer todo el zip en la memoria y hacer acceso aleatorio en él. Esto significa que impondrá demandas irrazonables a la memoria del sistema para archivos zip grandes. No es tanto un problema para un archivo zip más pequeño.

Además: no maneja el archivo zip "caso general" - hay muchas opciones zip que no me molesté en implementar en el descomprimidor - como cifrado ZIP, cifrado WinZip, zip64, nombres de archivo codificados en UTF-8, etc. en. ( EDITAR : ahora maneja nombres de archivo codificados en UTF-8). Sin embargo, la clase ZipFile maneja lo básico. Algunas de estas cosas no serían difíciles de implementar. Tengo una clase de cifrado AES en Javascript; que podría integrarse para admitir el cifrado. La compatibilidad con Zip64 probablemente sería inútil para la mayoría de los usuarios de Javascript, ya que está destinada a admitir archivos zip> 4gb, no es necesario extraerlos en un navegador.

Tampoco probé el caso para descomprimir contenido binario. Ahora mismo descomprime el texto. Si tiene un archivo binario comprimido, necesitará editar la clase ZipFile para manejarlo correctamente. No descubrí cómo hacerlo limpiamente. Ahora también hace archivos binarios.


EDITAR - Actualicé la biblioteca de descomprimir JS y la demostración. Ahora hace archivos binarios, además de texto. Lo he hecho más resistente y más general: ahora puede especificar la codificación que se usará al leer archivos de texto. También se amplía la demostración: muestra cómo descomprimir un archivo XLSX en el navegador, entre otras cosas.

Entonces, aunque creo que tiene una utilidad e interés limitados, funciona. Supongo que funcionaría en Node.js.


Esto se ve muy bien pero recibí este mensaje de error: este archivo zip usa UTF8, que no es compatible con ZipFile.js. ¿Alguna solución rápida que pueda recomendar?
Giulio Prisco

@Giulio - ok, modifiqué la clase ZipFile para admitir la decodificación de nombres de archivo codificados en UTF8. Debería funcionar ahora.
Cheeso

¡Increíble! ¿Puede agregar compatibilidad con KMZ (Binary) y KML (XML) a JSIO.guessFileType?
Brendan Byrd

1
Tengo una versión antigua de una de las demostraciones en línea , pero vine aquí buscando actualizaciones. @Cheeso Estaría interesado en enlaces actualizados cuando tenga tiempo.
geocodezip

1
@DannyBeckett - ok, gracias por el recordatorio y la sugerencia. Pronto pondré la demostración en algún lugar.
Cheeso

26

Estoy usando zip.js y parece ser bastante útil. ¡Vale la pena echarle un vistazo!

Consulte la demostración de Unzip , por ejemplo.


Estoy usando zip.js de la misma manera que usas, pero en safari obtengo que el lector de archivos no está definido. Por favor, ayúdame a trabajar con safari.
user969275

No tengo experiencia con Safari. Debe preguntar a los desarrolladores de zip.js. Hay una dirección de correo electrónico en la parte inferior de la página del proyecto: gildas-lormeau.github.com/zip.js . Quizás sea un error, así que te agradecerán que lo notifiques.
Dani BISHOP

Gracias por responder, he publicado un problema.
user969275

Tengo archivos JSON con una cadena JSON codificada en base64 en formato zip dentro de ellos. Necesito ese objeto JSON interno. InflatorInputStream de Java puede descomprimirlo en el servidor, por lo que en realidad está en formato zip. Sin embargo, cuando paso los datos decodificados de base64 de atob () a zip.js usando BlobReader, aparece "Error al leer el archivo zip". error. Visualmente, la salida de atob () es binaria, por lo que BlobReader parece correcto, probé TextReader de todos modos, da "El formato de archivo no se reconoce". ¿Algunas ideas?
enigment

Resolví mi problema en una línea de código con pako pako.inflate(binaryData, { to: 'string' })
enigment

17

Encontré jszip bastante útil. Los he usado hasta ahora solo para leer, pero también tienen capacidades de creación / edición.

En cuanto al código, se parece a esto

var new_zip = new JSZip();
new_zip.load(file);
new_zip.files["doc.xml"].asText() // this give you the text in the file

Una cosa que noté es que parece que el archivo tiene que estar en formato de flujo binario (lea usando el .readAsArrayBuffer de FileReader (), de lo contrario, recibía errores que indicaban que podría tener un archivo zip corrupto

Editar: Nota de la guía de actualización 2.xa 3.0.0 :

El método load () y el constructor con datos (nuevo JSZip (datos)) han sido reemplazados por loadAsync ().

Gracias user2677034


1
Eso me ayudó. Gracias. :)
deekshith

1
Este método se ha eliminado en JSZip 3.0, consulte la guía de actualización.
user2677034

1
Gracias tío, es una biblioteca increíble, porque es muy fácil de usar (en comparación con las respuestas anteriores).
Maxim Georgievskiy

5

Si también necesita admitir otros formatos o simplemente necesita un buen rendimiento, puede usar esta biblioteca de WebAssembly

está basado en promesas, utiliza WebWorkers para subprocesos y la API es en realidad un módulo ES simple



2

El ejemplo de código se da en el sitio del autor . Puede utilizar babelfish para traducir los textos (del japonés al inglés).

Por lo que entiendo japonés, este código de inflado postal está destinado a decodificar datos ZIP (flujos), no archivos ZIP.



0

Si alguien está leyendo imágenes u otros archivos binarios de un archivo zip alojado en un servidor remoto, puede usar el siguiente fragmento para descargar y crear un objeto zip usando la biblioteca jszip .

// this function just get the public url of zip file.
let url = await getStorageUrl(path) 
console.log('public url is', url)
//get the zip file to client
axios.get(url, { responseType: 'arraybuffer' }).then((res) => {
  console.log('zip download status ', res.status)
//load contents into jszip and create an object
  jszip.loadAsync(new Blob([res.data], { type: 'application/zip' })).then((zip) => {
    const zipObj = zip
    $.each(zip.files, function (index, zipEntry) {
    console.log('filename', zipEntry.name)
    })
  })

Ahora, usando zipObj, puede acceder a los archivos y crear una URL src para ellos.

var fname = 'myImage.jpg'
zipObj.file(fname).async('blob').then((blob) => {
var blobUrl = URL.createObjectURL(blob)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.