Загрузка файла с Java (с индикатором выполнения) - PullRequest
30 голосов
/ 31 октября 2008

Я чрезвычайно новый в Java, и в основном я только учил себя на ходу, поэтому я начал создавать апплет. Я хотел бы сделать тот, который может выбрать файл с локального диска и загрузить его как POST-запрос multipart / form-data, но с индикатором выполнения . Очевидно, что пользователь должен предоставить разрешение апплету Java для доступа к жесткому диску. Теперь у меня уже работает первая часть: пользователь может выбрать файл, используя объект JFileChooser, который удобно возвращает объект File. Но мне интересно, что будет дальше. Я знаю, что File.length() даст мне общий размер в байтах файла, но как я могу отправить выбранные File в сеть и как я могу отслеживать, сколько байтов было отправлено? Заранее спасибо.

Ответы [ 11 ]

39 голосов
/ 22 января 2009

Чтобы проверить прогресс с помощью HttpClient, оберните MultipartRequestEntity вокруг единицы, которая считает отправленные байты. Обертка ниже:

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

Затем реализует ProgressListener, который обновляет индикатор выполнения.
Помните, что обновление индикатора выполнения не должно выполняться в потоке отправки событий.

12 голосов
/ 06 сентября 2011

Более простая функция countingEntity не будет зависеть от конкретного типа сущности, а будет расширяться 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));
    }
}
6 голосов
/ 19 июля 2009

Я наткнулся на апплет Java-загрузчика с открытым исходным кодом и нашел все, что мне нужно было знать в его коде. Вот ссылки на сообщение в блоге, описывающее его, а также источник:

Статья
Исходный код

5 голосов
/ 26 апреля 2012

Количество байтов, возвращаемых слушателем, отличается от исходного размера файла. Поэтому вместо transferred++ я изменил его так, чтобы transferred=len; это длина фактического количества байтов, записываемых в выходной поток. И когда я вычисляю сложение всех переданных байтов, оно равно фактическому ContentLength, возвращенному CountingMultiPartEntity.this.getContentLength();

public void write(byte[] b, int off, int len) throws IOException {
    wrappedOutputStream_.write(b,off,len);
    transferred=len;
    listener_.transferred(transferred);
}
4 голосов
/ 01 ноября 2008

Имейте в виду, что индикатор выполнения может вводить в заблуждение, когда промежуточный компонент в сети (например, HTTP-прокси интернет-провайдера или обратный HTTP-прокси перед сервером) потребляет загрузку быстрее, чем сервер.

4 голосов
/ 01 ноября 2008

Вы можете найти эту статью полезной. Он подробно объясняет, как HttpClient и FileUpload, оба проекта apache, делают то, что вам нужно Он также включает примеры кода.

2 голосов
/ 01 июля 2010

Просто мой 2с стоит:

Это основано на ответе тьюлера (в момент написания сообщения была обнаружена ошибка). Я немного изменил его, так что вот моя версия ответа tuler и mmyers (кажется, я не могу отредактировать их ответ). Я хотел попытаться сделать это немного чище и быстрее. Помимо ошибки (которую я обсуждаю в комментариях к их ответу), большая проблема, которую я имею с их версией, заключается в том, что она создает новый CountingOutputStream при каждой записи. Это может стать очень дорогим с точки зрения памяти - тонны выделений и сборщиков мусора. Меньшая проблема заключается в том, что используется делегат, когда он может просто развернуть MultipartEntity. Не уверенный, почему они выбрали это, таким образом я сделал это способом, с которым я был более знаком. Если кто-нибудь знает плюсы / минусы двух подходов, это было бы здорово. Наконец, метод FilterOutputStream # write (byte [], int, int) просто вызывает запись FilterOutputStream # (byte) в цикле. Документация FOS рекомендует подклассам переопределять это поведение и делать его более эффективным. Лучший способ сделать это здесь - позволить исходному OutputStream обрабатывать запрос на запись.

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);
        }
    }
}
2 голосов
/ 01 ноября 2008

Как отмечается в статье, опубликованной Винсентом, вы можете использовать Apache Commons для этого.

Немного порезано


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

1 голос
/ 03 апреля 2016

Из других ответов вы можете просто переопределить AbstractHttpEntity дочерние классы или реализации public void writeTo(OutputStream outstream) метод, который вы используете, если не хотите создавать класс.

Пример использования 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]
            }
        });
    }

};
1 голос
/ 16 апреля 2009

Apache common - очень хороший вариант. Apache common позволяет настраивать следующие вещи.

  1. Настройка (файл xml) максимального размера файла / размера загружаемого файла
  2. Путь к месту назначения (куда сохранить загруженный файл)
  3. Установите темп. папка, чтобы поменять файл, чтобы загрузка файла была быстрой.
...