Загрузка файла с помощью Java (с индикатором выполнения)

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

Ответ 1

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

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

Ответ 2

Чтобы проверить прогресс с помощью 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, который обновляет индикатор выполнения.
Помните, что обновление потока выполнения не должно запускаться в потоке Dispatch Event.

Ответ 3

Простейший счетчикEntity не будет зависеть от конкретного типа объекта, а скорее продолжит 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));
    }
}

Ответ 4

Количество байтов, возвращаемых слушателем, отличается от исходного размера файла. Итак, вместо 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);
}

Ответ 5

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

Ответ 6

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

Ответ 7

Как отмечалось в статье Vincent, вы можете использовать общие права Apache для этого.

Маленький отрезанный


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

Ответ 8

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

Это основано на ответе тюлера (есть ошибка во время написания). Я немного изменил его, так что вот моя версия тюнера и mmyers (я не могу изменить их ответ). Я хотел попытаться сделать это немного чище и быстрее. Помимо ошибки (которую я обсуждаю в комментариях к их ответу), большой проблемой, которую я имею с их версией, является то, что она создает новый CountingOutputStream с каждой записью. Это может стать очень дорогостоящим с точки зрения памяти - тонны распределений и сборки мусора. Меньшая проблема заключается в том, что используется делегат, когда он может просто расширить MultipartEntity. Не уверен, почему они выбрали это, поэтому я сделал это так, как мне было знакомо. Если кто-то знает плюсы и минусы двух подходов, которые были бы замечательными. Наконец, метод FilterOutputStream # write (byte [], int, int) просто вызывает цикл WriteOutputStream # write (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 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);
        }
    }
}

Ответ 9

Посмотрите HTTP-клиент для выгрузки файла в сеть. Он должен уметь это делать. Я не уверен, как получить индикатор выполнения, но это потребует некоторого запроса этого API.

Ответ 10

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

  • Настроить (файл xml) максимальный размер файла/размер загружаемого файла
  • Целевой маршрут (где сохранить загруженный файл)
  • Задайте темп. для обмена файлами, чтобы загрузка файлов была быстрой.

Ответ 11

Из других ответов вы можете просто переопределить класс 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]
            }
        });
    }

};