Pergunta

Eu sou extremamente novo para Java, e, na sua maioria apenas foram me ensinando como eu ir, então eu comecei a construir um applet. Eu gostaria de fazer um que pode selecionar um arquivo a partir do disco local e enviá-lo como um pedido POST multipart / form-data, mas com uma barra de progresso . Obviamente, o usuário tem de conceder permissão ao applet Java para acessar o disco rígido. Agora eu já tenho o primeiro trabalho parte: o usuário pode selecionar um arquivo usando um objeto JFileChooser, que, convenientemente, retorna um objeto File. Mas eu estou querendo saber o que vem a seguir. Eu sei que File.length() vai me dar o tamanho total em bytes do arquivo, mas como faço para enviar o File selecionado para a web, e como faço para monitorar quantos bytes foram enviados? Agradecemos antecipadamente.

Foi útil?

Solução 3

acabei tropeçando um open source Java Uploader applet e encontrei tudo que eu precisava saber dentro de seu código. Aqui estão os links para um post descrevendo-a, bem como a fonte:

artigo
Código Fonte

Outras dicas

Para verificar o progresso usando HttpClient, enrole a MultipartRequestEntity cerca de um que conta os bytes a ser enviado. Wrapper está abaixo:

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

Em seguida, implementa um ProgressListener que atualiza uma barra de progresso.
Lembre-se que a atualização barra de progresso não deve ser executado no segmento de distribuição de eventos.

A countingEntity mais simples não depende do tipo de entidade específica, mas sim estender 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));
    }
}

A quantidade de bytes retornado pelo ouvinte é diferente do tamanho do arquivo original. Assim, em vez de ter transferred++, eu modifiquei para que transferred=len; que é o comprimento da quantidade real de bytes a ser escrito para o fluxo de saída. E quando eu calcular a adição do total de bytes transferidos é igual ao ContentLength real retornado 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);
}

Você pode achar este artigo útil. Ele explica em detalhe usando HttpClient e FileUpload, ambos apache projetos para fazer o que quiser. Ele também inclui exemplos de código.

Tenha em mente que a barra de progresso pode ser enganosa quando um componente intermediário na rede (por exemplo, proxy HTTP de um ISP, ou um proxy HTTP reverso na frente do servidor) consome seu upload mais rápido do que o servidor faz.

Como observou o artigo Vincent publicado, você pode usar commons Apache para fazer isso.

Little cortou


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

Apenas meu valor 2c:

Isto é baseado fora da resposta de Tuler (tem um bug no momento da escrita). Eu modifiquei um pouco, por isso aqui é minha versão de resposta Tuler e mmyers (eu não consigo editar a sua resposta). Eu queria tentar fazer este pouco mais limpo uma e mais rápido. Além do bug (que eu discuto em comentários sobre a sua resposta), o grande problema que tenho com a sua versão é que ele cria um novo CountingOutputStream com cada gravação. Isso pode ficar muito caro em termos de memória - toneladas de alocações e coletas de lixo. questão menor é que é usos um delegado quando poderia simplesmente expandir o MultipartEntity. Não sei por que eles escolheram isso, então eu fiz isso de uma maneira que eu estava mais familiarizado. Se alguém souber vantagens / desvantagens das duas abordagens que seria ótimo. Finalmente, o FilterOutputStream # write (byte [], int, int) apenas chama o # write FilterOutputStream (byte) em um loop. A FOS documentação recomenda subclasses substituindo esse comportamento e fazer isso mais eficiente. A melhor maneira de fazer isso aqui é deixar o OutputStream subjacente lidar com o pedido escrito.

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

HTTP cliente para uploadign o arquivo para a web. Ele deve ser capaz de fazer isso. Estou inseguro como obter a barra de progresso, mas isso implicaria a consulta que API de alguma forma.

Apache comum é muito boa opção. Apache comum permite que você configure as seguintes coisas.

  1. Configure (arquivo XML) o tamanho máximo do arquivo tamanho do arquivo / upload
  2. O caminho de destino (onde deseja salvar o arquivo carregado)
  3. Definir a temperatura. pasta de trocar o arquivo, para que o upload do arquivo seria rápido.

De outras respostas você pode simplesmente substituir o crianças de classe AbstractHttpEntity ou implementações método public void writeTo(OutputStream outstream) você estiver usando, se não quiser criar uma classe.

Um exemplo usando uma instância 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top