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