subir - Carga de archivos con Java(con barra de progreso)
subir un archivo java (11)
Soy extremadamente nuevo en Java y, en su mayoría, me he estado enseñando solo a medida que avanzo, así que comencé a crear un applet. Me gustaría crear uno que pueda seleccionar un archivo del disco local y cargarlo como una solicitud POST multipart / form-data pero con una barra de progreso . Obviamente, el usuario debe otorgar permiso al applet de Java para acceder al disco duro. Ahora ya tengo funcionando la primera parte: el usuario puede seleccionar un archivo utilizando un objeto JFileChooser
, que devuelve convenientemente un objeto File
. Pero me pregunto qué vendrá después. Sé que File.length()
me dará el tamaño total en bytes del archivo, pero ¿cómo envío el File
seleccionado a la web y cómo puedo monitorear cuántos bytes se han enviado? Gracias por adelantado.
A partir de las otras respuestas, puede anular el método de los niños de la clase AbstractHttpEntity
o las implementaciones public void writeTo(OutputStream outstream)
que está utilizando si no desea crear una clase.
Un ejemplo con una instancia de 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]
}
});
}
};
Apache común es una muy buena opción. Apache común le permite configurar lo siguiente.
- Configure (archivo xml) el tamaño máximo de archivo / tamaño de archivo de carga
- Ruta de destino (donde guardar el archivo cargado)
- Establecer la temperatura carpeta para intercambiar el archivo, de modo que la carga del archivo sea rápida.
Como se señala en el artículo publicado por Vincent, puede usar Apache commons para hacer esto.
Poco cortado
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();
}
}
Es posible que este article útil. Explica en detalle usando HttpClient y FileUpload, ambos proyectos de apache para hacer lo que quiera. También incluye muestras de código.
La cantidad de bytes devueltos por el oyente es diferente del tamaño del archivo original. Entonces, en lugar de haber transferred++
, lo modifiqué para que transferred=len
; esa es la longitud de la cantidad real de bytes que se escriben en la secuencia de salida. Y cuando calculo la suma del total de bytes transferidos, es igual al ContentLength
actual devuelto por CountingMultiPartEntity.this.getContentLength();
public void write(byte[] b, int off, int len) throws IOException {
wrappedOutputStream_.write(b,off,len);
transferred=len;
listener_.transferred(transferred);
}
Mire en HTTP Client para cargar el archivo a la web. Debería ser capaz de hacer eso. No estoy seguro de cómo obtener la barra de progreso, pero implicaría consultar esa API de alguna manera.
Para verificar el progreso usando HttpClient, ajuste MultipartRequestEntity alrededor de uno que cuente los bytes que se envían. Wrapper está abajo:
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);
}
}
}
Luego implementa un ProgressListener que actualiza una barra de progreso.
Recuerde que la actualización de la barra de progreso no debe ejecutarse en el subproceso de envío de evento.
Solo mi valor de 2c:
Esto se basa en la respuesta de tuler (tiene un error al momento de escribir). Lo modifiqué un poco, así que aquí está mi versión de Tuler y la respuesta de mmyers (parece que no puedo editar su respuesta). Quería intentar hacer esto un poco más limpio y más rápido. Además del error (que analizo en los comentarios sobre su respuesta), el gran problema que tengo con su versión es que crea un nuevo CountingOutputStream
con cada escritura. Esto puede ser muy costoso en términos de memoria: toneladas de asignaciones y recolección de basura. El problema menor es que usa un delegado cuando simplemente podría expandir MultipartEntity
. No estoy seguro de por qué eligieron eso, así que lo hice de una manera que estaba más familiarizado. Si alguien conoce las ventajas y desventajas de los dos enfoques, sería genial. Finalmente, el método FilterOutputStream # write (byte [], int, int) simplemente llama a FilterOutputStream # write (byte) en un bucle. La documentación de FOS recomienda subclases que anulan este comportamiento y lo hacen más eficiente. La mejor manera de hacerlo aquí es dejar que OutputStream subyacente maneje la solicitud de escritura.
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);
}
}
}
Tenga en cuenta que la barra de progreso puede ser engañosa cuando un componente intermedio en la red (por ejemplo, un proxy HTTP de un ISP o un proxy HTTP inverso en frente del servidor) consume su carga más rápido que el servidor.
Terminé tropezando con un applet de carga de código abierto de Java y encontré todo lo que necesitaba saber dentro de su código. Aquí hay enlaces a una publicación de blog que lo describe, así como la fuente:
Una conteo más HttpEntityWrapped
no dependería de un tipo de entidad específico, sino que se extendería 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));
}
}