Pregunta

Soy extremadamente nuevo para Java, y en su mayoría me he estado enseñando sobre la marcha, así que empecé a construir 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 tiene que otorgar permiso al applet de Java para acceder al disco duro. Ahora ya tengo la primera parte en funcionamiento: el usuario puede seleccionar un archivo utilizando un objeto JFileChooser , que convenientemente devuelve un objeto File . Pero me pregunto qué viene 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 hago? monitorear cuantos bytes han sido enviados? Gracias de antemano.

¿Fue útil?

Solución 3

Terminé tropezando con un applet de carga Java de código abierto y encontré todo lo que necesitaba saber dentro de su código. Aquí hay enlaces a una publicación del blog que lo describe, así como la fuente:

Artículo
Código fuente

Otros consejos

Para verificar el progreso usando HttpClient, envuelva MultipartRequestEntity alrededor de uno que cuenta los bytes que se envían. La envoltura 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 distribución de eventos.

Una entidad de conteo más simple no dependería de un tipo de entidad específica, sino que ampliarí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));
    }
}

La cantidad de bytes devueltos por el oyente es diferente del tamaño del archivo original. Entonces, en lugar de que se haya transferido ++ , lo modifiqué para que transfer = len ; esa es la longitud de la cantidad real de bytes que se escriben en el flujo de salida. Y cuando calculo la suma del total de bytes transferidos es igual al ContentLength real 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);
}

Puede encontrar este artículo útil. Explica en detalle el uso de HttpClient y FileUpload, ambos proyectos de apache para hacer lo que usted desea. También incluye ejemplos de código.

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 frente al servidor) consume su carga más rápido que el servidor.

Como lo señaló el artículo publicado por Vincent, puedes usar Apache commons para hacer esto.

Pequeño recortado


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

Sólo mi valor 2c:

Esto se basa en la respuesta de Tuler (tiene un error al momento de escribir). Lo modifiqué ligeramente, así que aquí está mi versión de tuler y mmyers respuesta (parece que no puedo editar su respuesta). Quería intentar hacer esto un poco más limpio y rápido. Además del error (que comento 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 resultar muy costoso en términos de memoria: toneladas de asignaciones y recolecciones de basura. El problema más pequeño es que utiliza un delegado cuando podría expandir la MultipartEntity . No estoy seguro de por qué eligieron eso, así que lo hice de una manera con la que estaba más familiarizado. Si alguien sabe los pros / contras de los dos enfoques que sería genial. Finalmente, el método FilterOutputStream # write (byte [], int, int) simplemente llama a la FilterOutputStream # write (byte) en un bucle. La documentación de FOS recomienda el reemplazo de subclases Este comportamiento y hacer esto más eficiente. La mejor manera de hacerlo aquí es dejar que el 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);
        }
    }
}

Busque en Cliente HTTP para cargar el archivo en la web. Debería poder hacer eso. No estoy seguro de cómo obtener la barra de progreso, pero implicaría consultar esa API de alguna manera.

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

  1. Configure (archivo xml) el tamaño máximo de archivo / tamaño de archivo de carga
  2. Ruta de destino (donde guardar el archivo cargado)
  3. Establecer la temperatura. carpeta para intercambiar el archivo, por lo que la carga del archivo sería rápida.

A partir de las otras respuestas, puede anular el método AbstractHttpEntity o implementaciones public void writeTo (OutputStream outstream) que está utilizando si no desea crear una clase .

Un ejemplo utilizando 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]
            }
        });
    }

};
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top