Téléchargement de fichiers avec Java (avec barre de progression))
je suis extrêmement nouveau à Java, et ont pour la plupart été simplement l'enseignement de moi-même que je m'en vais, j'ai donc commencé la construction d'une applet. J'aimerais en faire un qui peut sélectionner un fichier à partir du disque local et le télécharger comme multipart/form-data POST request mais avec une barre de progression . Évidemment, l'utilisateur doit accorder la permission à L'applet Java pour accéder au disque dur. Maintenant j'ai déjà la première partie qui fonctionne: l'utilisateur peut sélectionner un fichier en utilisant un JFileChooser
objet, qui retourne commodément un File
objet. Mais je me demandais ce qui s'en vient. Je sais que File.length()
va me donner la taille totale, en octets du fichier, mais comment puis-je envoyer la sélection File
sur le web, et comment puis-je surveiller combien d'octets ont été envoyés? Merci à l'avance.
11 réponses
pour vérifier la progression en utilisant HttpClient, entourer la Multipartrequestity d'une entité qui compte les octets envoyés. L'emballage est ci-dessous:
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.commons.httpclient.methods.RequestEntity;
public class CountingMultipartRequestEntity implements RequestEntity {
private final RequestEntity delegate;
private final ProgressListener listener;
public CountingMultipartRequestEntity(final RequestEntity entity,
final ProgressListener listener) {
super();
this.delegate = entity;
this.listener = listener;
}
public long getContentLength() {
return this.delegate.getContentLength();
}
public String getContentType() {
return this.delegate.getContentType();
}
public boolean isRepeatable() {
return this.delegate.isRepeatable();
}
public void writeRequest(final OutputStream out) throws IOException {
this.delegate.writeRequest(new CountingOutputStream(out, this.listener));
}
public static interface ProgressListener {
void transferred(long num);
}
public static class CountingOutputStream extends FilterOutputStream {
private final ProgressListener listener;
private long transferred;
public CountingOutputStream(final OutputStream out,
final ProgressListener listener) {
super(out);
this.listener = listener;
this.transferred = 0;
}
public void write(byte[] b, int off, int len) throws IOException {
out.write(b, off, len);
this.transferred += len;
this.listener.transferred(this.transferred);
}
public void write(int b) throws IOException {
out.write(b);
this.transferred++;
this.listener.transferred(this.transferred);
}
}
}
implémente alors un ProgressListener qui met à jour une barre de progression.
Rappelez-vous que la mise à jour de la barre de progression ne doit pas s'exécuter sur le Thread Event Dispatch.
une entité comptable plus simple ne dépendrait pas d'un type d'entité spécifique, mais étendrait plutôt HttpEntityWrapped
:
package gr.phaistos.android.util;
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.HttpEntity;
import org.apache.http.entity.HttpEntityWrapper;
public class CountingHttpEntity extends HttpEntityWrapper {
public static interface ProgressListener {
void transferred(long transferedBytes);
}
static class CountingOutputStream extends FilterOutputStream {
private final ProgressListener listener;
private long transferred;
CountingOutputStream(final OutputStream out, final ProgressListener listener) {
super(out);
this.listener = listener;
this.transferred = 0;
}
@Override
public void write(final byte[] b, final int off, final int len) throws IOException {
//// NO, double-counting, as super.write(byte[], int, int) delegates to write(int).
//super.write(b, off, len);
out.write(b, off, len);
this.transferred += len;
this.listener.transferred(this.transferred);
}
@Override
public void write(final int b) throws IOException {
out.write(b);
this.transferred++;
this.listener.transferred(this.transferred);
}
}
private final ProgressListener listener;
public CountingHttpEntity(final HttpEntity entity, final ProgressListener listener) {
super(entity);
this.listener = listener;
}
@Override
public void writeTo(final OutputStream out) throws IOException {
this.wrappedEntity.writeTo(out instanceof CountingOutputStream? out: new CountingOutputStream(out, this.listener));
}
}
j'ai fini par tomber sur une applet Java uploader open source et j'ai trouvé tout ce que je devais savoir dans son code. Voici les liens vers un billet de blog qui le décrit ainsi que la source:
le nombre d'octets retournés par l'auditeur est différent de la taille du fichier original. Ainsi, au lieu d'avoir transferred++
, je l'ai modifié de sorte que transferred=len
; c'est la longueur de la quantité réelle d'octets écrits dans le flux de sortie. Et quand je calcule l'addition des octets totaux transférés il est égal au ContentLength
réel retourné par CountingMultiPartEntity.this.getContentLength();
public void write(byte[] b, int off, int len) throws IOException {
wrappedOutputStream_.write(b,off,len);
transferred=len;
listener_.transferred(transferred);
}
vous pourriez trouver ce article utile. Il explique en détail en utilisant HttpClient et FileUpload, les deux projets apache pour faire ce que vous voulez. Il comprend également des échantillons de code.
gardez à l'esprit que la barre de progression peut être trompeuse lorsqu'un composant intermédiaire du réseau (par exemple, un mandataire HTTP D'un FAI, ou un mandataire HTTP inverse devant le serveur) consomme votre téléchargement plus rapidement que le serveur.
comme le note L'article Vincent, Vous pouvez utiliser Apache commons pour cela.
Peu ciselée
DiskFileUpload upload = new DiskFileUpload();
upload.setHeaderEncoding(ConsoleConstants.UTF8_ENCODING);
upload.setSizeMax(1000000);
upload.setSizeThreshold(1000000);
Iterator it = upload.parseRequest((HttpServletRequest) request).iterator();
FileItem item;
while(it.hasNext()){
item = (FileItem) it.next();
if (item.getFieldName("UPLOAD FIELD"){
String fileName = item.getString(ConsoleConstants.UTF8_ENCODING);
byte[] fileBytes = item.get();
}
}
Juste ma 2c en vaut la peine:
ceci est basé sur la réponse de tuler(a un bug au moment de l'écriture). Je l'ai légèrement modifié, donc voici ma version de la réponse de tuler et mmyers (Je ne semble pas pouvoir modifier leur réponse). Je voulais tenter de faire un peu plus propre et plus rapide. Outre le bug (dont je discute dans les commentaires sur leur réponse), le gros problème que j'ai avec leur version est qu'il crée un nouveau CountingOutputStream
avec chaque écriture. Cela peut devenir très coûteux en termes de mémoire - des tonnes d'allocations et de la collecte des poubelles. Petit problème est qu'il utilise un délégué quand il pourrait se étendre le MultipartEntity
. Je ne sais pas pourquoi ils ont choisi cela, alors je l'ai fait d'une manière que je connaissais mieux. Si quelqu'un sait avantages/inconvénients des deux approches, ce serait super. Enfin, la méthode FilterOutputStream#write(byte [], int, int) appelle simplement le FilterOutputStream#write(byte) dans une boucle. La FOS documentation recommande des sous-classes et rendre cela plus efficace. La meilleure façon de le faire ici est de laisser le flux de sortie sous-jacent gérer la demande d'écriture.
import java.io.FilterOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
public class CountingMultiPartEntity extends MultipartEntity {
private UploadProgressListener listener_;
private CountingOutputStream outputStream_;
private OutputStream lastOutputStream_;
// the parameter is the same as the ProgressListener class in tuler's answer
public CountingMultiPartEntity(UploadProgressListener listener) {
super(HttpMultipartMode.BROWSER_COMPATIBLE);
listener_ = listener;
}
@Override
public void writeTo(OutputStream out) throws IOException {
// If we have yet to create the CountingOutputStream, or the
// OutputStream being passed in is different from the OutputStream used
// to create the current CountingOutputStream
if ((lastOutputStream_ == null) || (lastOutputStream_ != out)) {
lastOutputStream_ = out;
outputStream_ = new CountingOutputStream(out);
}
super.writeTo(outputStream_);
}
private class CountingOutputStream extends FilterOutputStream {
private long transferred = 0;
private OutputStream wrappedOutputStream_;
public CountingOutputStream(final OutputStream out) {
super(out);
wrappedOutputStream_ = out;
}
public void write(byte[] b, int off, int len) throws IOException {
wrappedOutputStream_.write(b,off,len);
++transferred;
listener_.transferred(transferred);
}
public void write(int b) throws IOException {
super.write(b);
}
}
}
regarder dans client HTTP pour télécharger le fichier vers le web. Il devrait être en mesure de le faire. Je ne sais pas comment obtenir la barre de progression, mais cela impliquerait de questionner cette API d'une façon ou d'une autre.
Apache common est une très bonne option. Apache common vous permet de configurer les choses suivantes.
- Configurer(fichier xml) la taille maximale du fichier/ télécharger le fichier "
- chemin de Destination (où sauvegarder le fichier téléchargé)
- fixe la température. dossier pour échanger le fichier , de sorte que l'upload de fichier rapide.
des autres réponses, vous pouvez simplement outrepasser la méthode AbstractHttpEntity
ou implémentations public void writeTo(OutputStream outstream)
que vous utilisez si vous ne voulez pas créer une classe.
exemple utilisant une instance FileEntity
:
FileEntity fileEntity = new FileEntity(new File("img.jpg")){
@Override
public void writeTo(OutputStream outstream) throws IOException {
super.writeTo(new BufferedOutputStream(outstream){
int writedBytes = 0;
@Override
public synchronized void write(byte[] b, int off, int len) throws IOException {
super.write(b, off, len);
writedBytes+=len;
System.out.println("wrote: "+writedBytes+"/"+getContentLength()); //Or anything you want [using other threads]
}
});
}
};