Rozpakowywanie plików


79

Chcę wyświetlać pliki OpenOffice , .odt i .odp po stronie klienta za pomocą przeglądarki internetowej.

Te pliki są plikami spakowanymi. Używając Ajax, mogę pobrać te pliki z serwera, ale są to pliki spakowane. Muszę je rozpakować za pomocą JavaScript , próbowałem użyć inflate.js, http://www.onicos.com/staff/iz/amuse/javascript/expert/inflate.txt , ale bez powodzenia.

Jak mogę to zrobić?


7
„brak sukcesu”, proszę sprecyzować, pokazać nam kod, pokazać nam błędy… jesteśmy tutaj, aby pomóc, a nie zgadywać.
OcuS

W zasadzie właśnie nazwałem funkcję inflate - data = zip_inflate (src); Ale myślę, że jest to przeznaczone dla pojedynczego pliku. Jeśli pliki zip zawierają wiele plików w strukturze katalogów, jaka będzie zawartość „danych”. Nie wiem, jak korzystać z tej biblioteki.
user69260

@Eimantas co to znaczy? + Lub -
user69260

4
@techfandu: (1) Kliknij swoje imię. (2) Kliknij poprzednie pytanie, które zadałeś. (3) Zaakceptuj odpowiedź, która najbardziej Ci pomogła. (4) Powtarzaj, aż wszystkie pytania będą miały akceptowaną odpowiedź.
Dave Jarvis

czy praca ci się powiodła? Muszę zrobić to samo z projektem w szkole (zagraj w odp w przeglądarce internetowej) .. Gdybyś mógł dać mi kilka wskazówek, byłoby super.
Alexx

Odpowiedzi:


61

Napisałem unzipper w Javascript. To działa.

Opiera się na czytniku plików binarnych Andy GP Na i logice inflacji RFC1951 z notmasteryet . Dodałem klasę ZipFile.

działający przykład:
http://cheeso.members.winisp.net/Unzip-Example.htm (martwy link)

Źródło:
http://cheeso.members.winisp.net/srcview.aspx?dir=js-unzip (martwy link)

Uwaga : linki są martwe; Niedługo znajdę nowego gospodarza.

W źródle znajduje się strona demonstracyjna ZipFile.htm oraz 3 różne skrypty, jeden dla klasy zipfile, jeden dla klasy inflate i jeden dla klasy czytnika plików binarnych. Demo zależy również od interfejsu użytkownika jQuery i jQuery. Jeśli po prostu pobierzesz plik js-zip.zip, znajdziesz tam całe potrzebne źródło.


Oto jak wygląda kod aplikacji w 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));
    }
}

Demo działa w kilku krokach: readFilefn jest wyzwalany przez kliknięcie i tworzy instancję obiektu ZipFile, który odczytuje plik zip. Istnieje asynchroniczne wywołanie zwrotne po zakończeniu odczytu (zwykle dzieje się to w mniej niż sekundę dla zamków błyskawicznych o rozsądnych rozmiarach) - w tym demie wywołanie zwrotne jest przechowywane w zmiennej lokalnej doneReading, która po prostu wywołuje extractEntries, która po prostu ślepo rozpakowuje całą zawartość podanego plik zip. W prawdziwej aplikacji prawdopodobnie wybrałbyś niektóre wpisy do wyodrębnienia (pozwoliłbyś użytkownikowi wybrać lub wybrać programowo jeden lub więcej wpisów itp.).

W extractEntriesfn iteracje nad wszystkimi wpisami, a rozmowy extract()na temat każdego z nich, mijając zwrotnego. Dekompresja wpisu zajmuje trochę czasu, może 1 s lub więcej dla każdego wpisu w pliku zip, co oznacza, że ​​asynchronia jest odpowiednia. Wyodrębnianie wywołania zwrotnego po prostu dodaje wyodrębnioną zawartość do akordeonu jQuery na stronie. Jeśli zawartość jest binarna, zostanie odpowiednio sformatowana (nie pokazana).


Działa, ale myślę, że narzędzie jest nieco ograniczone.

Po pierwsze: jest bardzo powolny. Rozpakowanie 140k pliku AppNote.txt z PKWare zajmuje około 4 sekund. To samo rozpakowanie można wykonać w czasie krótszym niż .5s w programie .NET. EDYCJA : Javascript ZipFile rozpakowuje się znacznie szybciej niż teraz, w IE9 i Chrome. Jest nadal wolniejszy niż program skompilowany, ale jest wystarczająco szybki dla normalnego użytkowania przeglądarki.

Po drugie: nie obsługuje przesyłania strumieniowego. Zasadniczo siorbi całą zawartość pliku zip do pamięci. W „prawdziwym” środowisku programistycznym można było wczytać tylko metadane pliku zip (powiedzmy 64 bajty na wpis), a następnie odczytać i rozpakować inne dane według potrzeb. O ile wiem, nie ma sposobu, aby zrobić takie IO w javascript, dlatego jedyną opcją jest wczytanie całego pliku zip do pamięci i zrobienie w nim swobodnego dostępu. Oznacza to, że duże pliki zip będą wymagały nieracjonalnego wykorzystania pamięci systemowej. Nie jest to duży problem w przypadku mniejszego pliku zip.

Ponadto: nie obsługuje pliku zip „ogólnego przypadku” - istnieje wiele opcji zip, których nie zawracałem sobie głowy zaimplementowaniem w rozpakowywaczu - takie jak szyfrowanie ZIP, szyfrowanie WinZip, zip64, zakodowane nazwy plików w formacie UTF-8 itd. na. ( EDYCJA - teraz obsługuje nazwy plików zakodowane w UTF-8). Klasa ZipFile obsługuje jednak podstawy. Niektóre z tych rzeczy nie byłyby trudne do wdrożenia. Mam klasę szyfrowania AES w JavaScript; które można by zintegrować w celu obsługi szyfrowania. Obsługa Zip64 byłaby prawdopodobnie bezużyteczna dla większości użytkowników Javascript, ponieważ jest przeznaczona do obsługi plików zip> 4 GB - nie trzeba ich rozpakowywać w przeglądarce.

Nie testowałem też przypadku rozpakowywania zawartości binarnej. W tej chwili rozpakowuje tekst. Jeśli masz spakowany plik binarny, musisz edytować klasę ZipFile, aby poprawnie go obsłużyć. Nie wymyśliłem, jak to zrobić czysto. Teraz obsługuje również pliki binarne.


EDYCJA - zaktualizowałem bibliotekę rozpakowywania JS i wersję demonstracyjną. Teraz oprócz tekstu obsługuje pliki binarne. Uczyniłem go bardziej odpornym i bardziej ogólnym - możesz teraz określić kodowanie, które ma być używane podczas czytania plików tekstowych. Rozbudowano również demo - pokazuje m.in. rozpakowywanie pliku XLSX w przeglądarce.

Tak więc, chociaż myślę, że ma ograniczoną użyteczność i zainteresowanie, działa. Myślę, że zadziałaby w Node.js.


Wygląda świetnie, ale otrzymałem ten komunikat o błędzie: Ten plik zip używa UTF8, który nie jest obsługiwany przez ZipFile.js. Jakieś szybkie obejście, które możesz polecić?
Giulio Prisco,

@Giulio - ok, zmodyfikowałem klasę ZipFile, aby obsługiwała dekodowanie nazw plików zakodowanych w UTF8. Teraz powinno działać.
Cheeso

Niesamowite! Czy możesz dodać obsługę formatu KMZ (binarne) i KML (XML) do JSIO.guessFileType?
Brendan Byrd

1
Mam starą wersję jednej z wersji demonstracyjnych online , ale przyszedłem tutaj w poszukiwaniu aktualizacji. @ Cheeso Będziesz zainteresowany zaktualizowanymi linkami, gdy będziesz miał czas.
geocodezip

1
@DannyBeckett - ok, dzięki za przypomnienie i sugestię. Niedługo umieszczę gdzieś demo.
Cheeso

26

Używam zip.js i wydaje się, że jest całkiem przydatny. Warto zajrzeć!

Sprawdź na przykład demo Rozpakuj .


Używam zip.js tego samego, co używasz, ale w safari otrzymuję plikereader nie jest zdefiniowany. pls pomóż mi pracować z safari.
user969275

Nie mam doświadczenia z Safari. Powinieneś zapytać programistów zip.js. Na dole strony projektu znajduje się adres e-mail: gildas-lormeau.github.com/zip.js . Może to błąd, więc podziękują za powiadomienie.
Dani BISHOP

Dziękuję za odpowiedź, opublikowałem problem.
user969275

Mam pliki JSON z ciągiem JSON zakodowanym w formacie base64 w formacie zip. Potrzebuję tego wewnętrznego obiektu JSON. Java InflatorInputStream może rozpakować go na serwerze, więc w rzeczywistości jest w formacie zip. Jednak gdy przekazuję zdekodowane dane base64 z atob () do zip.js przy użyciu BlobReader, pojawia się komunikat „Błąd podczas odczytu pliku zip”. błąd. Wizualnie wynik atob () jest binarny, więc BlobReader wydaje się mieć rację, mimo to wypróbował TextReader, daje "Format pliku nie jest rozpoznawany.". Jakieś pomysły?
Enigment

Rozwiązałem mój problem w jednej linii kodu za pomocą pako pako.inflate(binaryData, { to: 'string' })
enigment.

17

Uważam, że jszip jest całkiem przydatny. Do tej pory używałem tylko do czytania, ale mają również możliwości tworzenia / edycji.

Kod mądry wygląda mniej więcej tak

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

Jedną rzeczą, którą zauważyłem, jest to, że wydaje się, że plik musi być w formacie strumienia binarnego (czytaj za pomocą .readAsArrayBuffer z FileReader (), w przeciwnym razie otrzymywałem błędy informujące, że mogę mieć uszkodzony plik zip

Edycja: Uwaga z przewodnika aktualizacji 2.x do 3.0.0 :

Metoda load () i konstruktor z danymi (new JSZip (data)) zostały zastąpione przez loadAsync ().

Dzięki user2677034


1
To mi pomogło. Dzięki. :)
deekshith

1
Ta metoda została usunięta w JSZip 3.0, sprawdź przewodnik aktualizacji.
user2677034

1
Dzięki stary, to niesamowita biblioteka, ponieważ jest super łatwa w użyciu (w porównaniu do poprzednich odpowiedzi)!
Maxim Georgievskiy

5

Jeśli potrzebujesz obsługiwać również inne formaty lub po prostu potrzebujesz dobrej wydajności, możesz użyć tej biblioteki WebAssembly

Jest oparty na obietnicy, wykorzystuje WebWorkers do obsługi wątków, a API jest w rzeczywistości prostym modułem ES



2

Przykład kodu jest podany na stronie autora . Możesz użyć babelfish do tłumaczenia tekstów (z japońskiego na angielski).

O ile rozumiem język japoński, ten kod nadmuchiwania ZIP ma na celu dekodowanie danych ZIP (strumieni), a nie archiwum ZIP.



0

Jeśli ktoś czyta obrazy lub inne pliki binarne z pliku zip hostowanego na serwerze zdalnym, możesz użyć następującego fragmentu kodu, aby pobrać i utworzyć obiekt zip za pomocą biblioteki 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)
    })
  })

Teraz używając zipObj możesz uzyskać dostęp do plików i utworzyć dla nich adres URL src.

var fname = 'myImage.jpg'
zipObj.file(fname).async('blob').then((blob) => {
var blobUrl = URL.createObjectURL(blob)
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.