Je suis extrêmement nouveau pour Java, et je viens surtout de m'enseigner au fur et à mesure, j'ai donc commencé à construire une applet. Je voudrais en créer un qui puisse sélectionner un fichier sur le disque local et le télécharger en tant que multipart/form-data POST mais avec une barre de progression. De toute évidence, l'utilisateur doit accorder la permission à l'applet Java pour accéder au disque dur. Maintenant, j'ai déjà la première partie de travail: l'utilisateur peut sélectionner un fichier en utilisant un JFileChooser
objet, qui renvoie commodément un objet File
. Mais je me demande ce qui vient ensuite. Je sais que File.length()
me donnera la taille totale en octets du fichier, mais comment puis-je envoyer le File
sélectionné sur le Web et comment contrôler le nombre d'octets envoyés? Merci d'avance.
J'ai fini par tomber sur une source ouverte Java uploader et j'ai trouvé tout ce que je devais savoir dans son code. Voici des liens vers un article de blog le décrivant ainsi que la source:
Pour vérifier la progression à l'aide de HttpClient, encapsulez le MultipartRequestEntity autour de celui qui compte les octets envoyés. Wrapper 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 ensuite un ProgressListener qui met à jour une barre de progression.
N'oubliez pas que la mise à jour de la barre de progression ne doit pas s'exécuter sur le thread de répartition des événements.
Une countingEntity 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));
}
}
La quantité d'octets renvoyée par l'écouteur est différente de la taille de fichier d'origine. Donc, au lieu d'avoir transferred++
, Je l'ai modifié pour que transferred=len
; c'est la longueur de la quantité réelle d'octets en cours d'écriture dans le flux de sortie. Et quand je calcule l'addition du nombre total d'octets 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 cela 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 exemples de code.
Gardez à l'esprit que la barre de progression peut être trompeuse lorsqu'un composant intermédiaire du réseau (par exemple, le proxy HTTP d'un FAI ou un proxy HTTP inversé devant le serveur) consomme votre téléchargement plus rapidement que le serveur.
Comme indiqué dans l'article publié par Vincent, vous pouvez utiliser Apache commons pour ce faire.
Peu coupé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 mon 2c vaut:
Ceci est basé sur la réponse de tuler (a un bug au moment de l'écriture). Je l'ai légèrement modifié, voici donc ma version de tuler et mmyers (je n'arrive pas à modifier leur réponse). Je voulais essayer de rendre cela un peu plus propre et plus rapide. Outre le bogue (dont je discute dans les commentaires sur leur réponse), le gros problème que j'ai avec leur version est qu'elle crée un nouveau CountingOutputStream
à chaque écriture. Cela peut devenir très coûteux en termes de mémoire - des tonnes d'allocations et de collecte de déchets. Le plus petit problème est qu'il utilise un délégué alors qu'il pourrait simplement développer 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 connaît les avantages/inconvénients des deux approches, ce serait formidable. Enfin, la méthode FilterOutputStream # write (byte [], int, int) appelle simplement la FilterOutputStream # write (byte) dans une boucle. Le documentation FOS recommande les sous-classes remplaçant ce comportement et le rendant plus efficace. La meilleure façon de le faire ici est de laisser le OutputStream 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);
}
}
}
Regardez dans client HTTP pour télécharger le fichier sur le Web. Il devrait pouvoir le faire. Je ne sais pas comment obtenir la barre de progression, mais cela impliquerait d'interroger cette API d'une manière ou d'une autre.
Apache common est une très bonne option. Apache common vous permet de configurer les choses suivantes.
À partir des autres réponses, vous pouvez simplement remplacer la méthode AbstractHttpEntity
enfants ou implémentations public void writeTo(OutputStream outstream)
que vous utilisez si vous ne souhaitez pas créer de classe.
Un 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]
}
});
}
};