2008-10-31 24 views
29

Soy extremadamente nuevo a Java, y he estado enseñándome sobre la marcha, 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.Subida de archivos con Java (con barra de progreso)

+9

Esto es sólo un comentario al margen, lo odio cuando las empresas utilizan los applets para proporcionar una mejor interfaz de subida y el de html. Creo que este es un problema del navegador web. No se puede confiar ciegamente en los applets que piden permiso, pero la mayoría de los usuarios lo dan de todos modos. Triste pero cierto. – Pyrolistical

+0

@Pyrolistical Estoy de acuerdo, pero desafortunadamente, el soporte del navegador para cargar archivos es _simplemente horrible. Si agrega la necesidad/deseo de hacer cualquier procesamiento del lado del cliente en esos archivos, no tiene otra opción que no sea usar un applet o flash. – cdeszaq

Respuesta

6

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. A continuación se presentan enlaces a un blog que lo describen, así como la fuente:

Article
Source Code

1

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.

4

Puede que este article le ayude. Explica en detalle usando HttpClient y FileUpload, ambos proyectos de apache para hacer lo que quiera. También incluye muestras de código.

2

Según lo observado por el artículo publicado por Vincent, puede utilizar Apache commons para hacer esto.

Poco cortó

 

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(); 
    } 
} 
 
4

Tenga en cuenta que la barra de progreso podría inducir a error cuando consume un componente intermedio en la red (por ejemplo, el proxy HTTP de un ISP, o un proxy HTTP inverso delante del servidor) su carga más rápido que el servidor.

37

Para verificar el progreso utilizando HttpClient, ajuste MultipartRequestEntity alrededor de uno que cuente los bytes que se envían. Wrapper está debajo:

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.

+0

¿Podría dar un ejemplo de ProgressListener que actualizará los componentes de Swing – kilonet

+2

por qué CountingMultipartRequestEntity debe ser envuelto alrededor de MultipartRequestEntity? – kilonet

+2

Chicos, su respuesta es incorrecta. En muchos casos, esto contará dos veces todos los bytes enviados. FilterOutputStream # write (byte [], int, int) simplemente llama a FOS.write (byte) en un bucle, por lo que cuenta dos veces todos los bytes. Además, FOS recomienda cambiar este comportamiento ya que no es eficiente. Ver mi respuesta, donde guardo el flujo subyacente y llamo a su método de escritura (byte [], int, int). – Hamy

1

Apache común es una muy buena opción. Apache común le permite configurar las siguientes cosas.

  1. Configurar (archivo XML) el tamaño máximo del archivo de tamaño de archivo/carga
  2. Ruta de destino (dónde guardar el archivo cargado)
  3. puesto la temperatura. carpeta para intercambiar el archivo, de modo que la carga del archivo sea rápida.
+0

¿Cuál de los componentes de Apache Commons? –

2

Sólo mi 2c pena:

Esto se basa apagado de la respuesta de Tuler (tiene un error en el momento de escritura). 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 más pequeño es que usa un delegado cuando simplemente podría expandir el 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. El FOS documentation 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); 
     } 
    } 
} 
+1

Esto tiene muchos más problemas que el pequeño que el otro tenía. – ColinD

12

Un countingEntity más simple no dependería de un tipo de entidad específica sino que se extienden 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)); 
    } 
} 
+0

¿cómo implementaría esto? ¿podría etiquetar si su respuesta :) –

+0

como esto \t \t \t oyente ProgressListener = new ProgressListener() \t \t \t { \t \t \t \t @ Override \t \t \t \t public void transferido (NUM) \t \t \t \t { \t \t \t \t \t parentTask.publicPublishProgress (num); \t \t \t \t} \t \t \t}; \t \t \t StringEntity se = new StringEntity (XML); \t \t \t CountingHttpEntity ce = new CountingHttpEntity (se, listener); \t \t \t httppost.setEntity (ce); –

5

La cantidad de bytes devuelto por el oyente es diferente del tamaño del archivo original. Entonces, en lugar de tener 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 puedo calcular la adición de los bytes totales transferida es igual a la real ContentLength 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); 
} 
1

De las otras respuestas que sólo puede anular los AbstractHttpEntity niños de clase o implementaciones public void writeTo(OutputStream outstream) método que está utilizando, si no lo hacen quiero crear una clase

Un ejemplo mediante una instancia 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] 
      } 
     }); 
    } 

}; 
Cuestiones relacionadas