2010-07-28 3 views
2

J'ai un petit nombre (2-10) de fichiers volumineux (6-15 Go) qui se compressent vraiment bien (4: 1). J'écris le client et le serveur en Java, et je veux envoyer les fichiers du client au serveur, de sorte que 1. le client compresse les fichiers comme ils sont envoyés (ie pas de fichier intermédiaire .zip est créé)
2. le contenu compressé sur le serveur se présente sous la forme d'un fichier bien formé (par exemple, un fichier .zip ou .tgz) de sorte qu'il peut être téléchargé «tel quel».
3. Le transfert peut être reprise si elle ne parvient pas à mi-chemin à travers
4. la reprise de transfert pourrait se produire dans un nouveau processus tout à faitReprise d'une compression échouée en utilisant Java

Les deux premiers peuvent être atteints assez facilement en utilisant des sockets java.io et java. util.zip ZipOutputStreams. Le troisième est celui qui me cause le chagrin. Le quatrième est vraiment juste le contexte.

Je suppose qu'une solution nécessitera peut-être une sorte de retransmission partielle ou de ré-analyse pour établir un dictionnaire, ou quelque chose comme ça.

Existe-t-il des bibliothèques Java prenant en charge la compression pouvant être reprise?

+0

Quelques personnes ont suggéré le découpage. C'est une bonne idée, sauf pour la condition (2) ci-dessus. Si je tronque, je n'ai aucun moyen (que je sache) de faire un fichier .zip/.gz valide à partir des morceaux. –

+0

Comme je l'ai dit, vous devrez soit stocker un fichier sur le client ou réassembler sur le serveur. La seule alternative consiste à charger efficacement le fichier entier en mémoire et à créer une version en mémoire du fichier sur le client de toute façon. – cletus

Répondre

2

Je n'ai trouvé aucune bibliothèque préfabriquée prenant en charge la compression pouvant être reprise de la manière requise. Il y a, cependant, beaucoup de morceaux disponibles sous des licences ouvertes pour écrire les vôtres. J'ai maintenant une solution client/serveur qui satisfait toutes les contraintes décrites dans la question. L'idée est similaire aux idées de segmentation décrites ci-dessus, mais le serveur gère le découpage et fait une certaine comptabilité qui mappe les morceaux compressés sur le client aux morceaux compressés sur le serveur. Il n'y a aucun fichier temporaire dans la solution. Le protocole de base est la suivante

 
(1) The client sends a manifest to the server, containing the 
    to-be contents of the zip file 
(2) The server sends back an ID for the manifest 
    Then repeatedly 
    (3) The client asks the server "is there anything still 
     required for the manifest with ID X" 
    (4) The server replies "no", or with a manifest entry 
     for a file in the manifest, plus a offset and length to send 
    (5) The client compresses that chunk and sends it (plus some 
     bookkeeping info) 
    (6) The server puts the chunk into the every growing zip file, 
     plus appropriate zip file crud. If the server orders 
     the chunks it asks the client for appropriately, this can 
     all be done by file appends. 

Le serveur met à jour le manifeste seulement chaque pas de temps 6 se termine avec succès, de sorte que les défaillances au cours des étapes 3-6 (y compris les accidents sur le serveur ou le client) peuvent être repris en toute sécurité (bien, plus ou moins).

Il y a quelques bits qui ont été un peu fastidieux dans la création de fichiers zip en bloc. La chose de base qui doit être réalisée est de trouver un algorithme de compression capable de chunk-capable. Dégonfler peut être utilisé de cette manière.

Les java ZipOutputStream et DeflaterOutputStream ne conviennent pas vraiment pour le dégonflage/zipping «en bloc», car ils ne permettent pas un rinçage arbitraire. Il existe une implémentation Java sous licence de style BSD de ZLib au http://www.jcraft.com/jzlib. Je ne l'ai pas référencé pour la vitesse, mais il donne la même sortie que l'implémentation Java. JZLib est génial, et supporte tous les modes de vidage de ZLib (contrairement à l'implémentation de java.util.zip.Deflate).

De plus, les fichiers Zip calculent un CRC pour chaque entrée. Ainsi, l'entrée manifeste à l'étape 4 contient un CRC «partiel», qui est mis à jour pour chaque segment et renvoyé dans les informations de comptabilité à l'étape 5. Il existe une implémentation du domaine public CRC pour Java au http://www.axlradius.com/freestuff/CRC32.java. Je l'ai comparé et il est aussi rapide que (et fournit des CRC équivalents) l'implémentation Java native.

Enfin, le format de fichier Zip est assez pernickety. J'ai réussi à assembler la plupart d'une mise en œuvre de la page wikipedia et http://www.pkware.com/documents/casestudies/APPNOTE.TXT. Bien qu'à un moment je ne pouvais pas travailler la bonne valeur pour l'un des champs. Heureusement, la source ZipOutputStream du JDK est disponible pour que vous puissiez voir ce qu'ils font.

0

Je ne connais rien qui permette de reprendre la compression au milieu d'un flux; cela semble être une chose très sensible à l'état. Au lieu de cela, vous pourriez envisager de "casser" le fichier en plus petits morceaux et de les envoyer individuellement (avec compression). Dites, morceaux 100kb (par exemple). Vous ne pouvez toujours pas reprendre au milieu d'un morceau, mais vous pouvez facilement commencer au début du morceau le plus récent.

0

La compression à la volée est facile. Le problème que vous allez avoir est de reprendre le téléchargement. Cela élimine fondamentalement le protocole HTTP en tant que moyen de transport, vous devrez donc regarder quelque chose comme (S) FTP ou SCP. Même là, le problème est que vous ne créez pas de fichier sur le client, alors qu'est-ce qui va être repris? À tout le moins, vous devrez utiliser une méthode de compression déterministe (ce qui signifie que, si un fichier spécifié est spécifié, deux exécutions de l'algorithme de compression produiront exactement la même sortie). Si ce n'est pas vrai, vous ne pouvez pas reprendre du tout.

Mon conseil est d'adopter une approche légèrement tangentielle. Diviser le fichier en morceaux gérables (disons 50 Mo). C'est déterministe. Compressez chaque tronçon individuellement. Si un bloc échoue, renvoyez-le. Il n'y a pas de reprise mais vous pouvez obtenir des téléchargements partiels par le serveur indiquant au client quels morceaux il a reçus ou attendus.

Un problème que vous aurez est d'identifier un fichier particulier. Est-ce que le nom de fichier va faire? Y a-t-il une autre caractéristique d'identification? Si deux clients essaient de télécharger le même fichier, le serveur pourra-t-il le détecter? L'approche standard pour ce genre de chose est d'utiliser une somme de contrôle (hachage SHA1 du contenu du fichier), mais vous ne voulez pas lire un fichier de 16 Go dans son intégralité juste pour faire une somme de contrôle. Donc, une autre méthode serait préférable.

Imaginez la communication réseau ressemble à ceci:

Client: SEND file1234 CHUNKS 167 
Server: RECEIVED (already got) or WAIT 7 (chunk #) 
Client: compress and send chunk 7 
Server: WAIT 8 
.... 

Cette méthode traitera également plusieurs clients téléchargeant le fichier en même temps que le serveur peut demander différents morceaux de différents clients et de les fusionner ensemble.

Le problème avec cette méthode est que le fichier n'est pas "complet" sur le serveur (comme un zip ou un tarball) mais je pense que vous devez abandonner pour finir avec quelque chose qui fonctionne réellement et non être un cauchemar à coder.

+0

Il est sûr de supposer qu'un fichier peut être identifié par son nom (c'est en fait un peu plus compliqué que cela, mais la négociation pour un ID est gérée ailleurs dans le système). (Aussi, s'il vous plaît voir le commentaire sur la question elle-même comme une réponse à l'idée de découpage) –