Подтвердить что ты не робот

Как получить индикатор выполнения для загрузки файла с помощью Apache HttpClient 4?

У меня есть следующий код для загрузки файла с HTTP-клиентом Apache (org.apache.http.client):

  public static void main(String[] args) throws Exception
  {
    String fileName = "test.avi";
    File file = new File(fileName);

    String serverResponse = null;
    HttpParams params = new BasicHttpParams();
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    HttpPut put = new HttpPut("http://localhost:8080/" + fileName);

    FileEntity fileEntity = new FileEntity(file, "binary/octet-stream");
    put.setEntity(fileEntity);   

    HttpResponse response = client.execute(put);
    HttpEntity entity = response.getEntity();
    if (entity != null)
    {
      serverResponse = EntityUtils.toString(entity);
      System.out.println(serverResponse);
    }
  }

Это работает хорошо, но теперь я хочу иметь индикатор выполнения, который показывает ход загрузки файла. Как это можно сделать? Я нашел фрагмент кода в Загрузка файла с помощью Java (с индикатором выполнения), но он предназначен для Apache HTTP Client 3 (org.apache.commons.httpclient) и RequestEntity класса не существует в Apache HTTP Client 4.; (

Может у кого-то из вас есть подход?

Много приветствий

Бенни

4b9b3361

Ответ 1

Привет, ребята!

Я сам решил проблему и сделал простой пример.
Если есть какие-либо вопросы, не стесняйтесь спрашивать.

Здесь мы идем!

ApplicationView.java

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpVersion;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPut;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.apache.http.util.EntityUtils;

public class ApplicationView implements ActionListener
{

  File file = new File("C:/Temp/my-upload.avi");
  JProgressBar progressBar = null;

  public ApplicationView()
  {
    super();
  }

  public void createView()
  {
    JFrame frame = new JFrame("File Upload with progress bar - Example");
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setBounds(0, 0, 300, 200);
    frame.setVisible(true);

    progressBar = new JProgressBar(0, 100);
    progressBar.setBounds(20, 20, 200, 30);
    progressBar.setStringPainted(true);
    progressBar.setVisible(true);

    JButton button = new JButton("upload");
    button.setBounds(progressBar.getX(),
            progressBar.getY() + progressBar.getHeight() + 20,
            100,
            40);
    button.addActionListener(this);

    JPanel panel = (JPanel) frame.getContentPane();
    panel.setLayout(null);
    panel.add(progressBar);
    panel.add(button);
    panel.setVisible(true);
  }

  public void actionPerformed(ActionEvent e)
  {
    try
    {
      sendFile(this.file, this.progressBar);
    }
    catch (Exception ex)
    {
      System.out.println(ex.getLocalizedMessage());
    }
  }

  private void sendFile(File file, JProgressBar progressBar) throws Exception
  {
    String serverResponse = null;
    HttpParams params = new BasicHttpParams();
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    HttpPut put = new HttpPut("http://localhost:8080/" + file.getName());

    ProgressBarListener listener = new ProgressBarListener(progressBar);
    FileEntityWithProgressBar fileEntity = new FileEntityWithProgressBar(file, "binary/octet-stream", listener);
    put.setEntity(fileEntity);

    HttpResponse response = client.execute(put);
    HttpEntity entity = response.getEntity();
    if (entity != null)
    {
      serverResponse = EntityUtils.toString(entity);
      System.out.println(serverResponse);
    }
  }
}

FileEntityWithProgressBar.java

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import org.apache.http.entity.AbstractHttpEntity;

/**
 * File entity which supports a progress bar.<br/>
 * Based on "org.apache.http.entity.FileEntity".
 * @author Benny Neugebauer (www.bennyn.de)
 */
public class FileEntityWithProgressBar extends AbstractHttpEntity implements Cloneable
{

  protected final File file;
  private final ProgressBarListener listener;
  private long transferredBytes;

  public FileEntityWithProgressBar(final File file, final String contentType, ProgressBarListener listener)
  {
    super();
    if (file == null)
    {
      throw new IllegalArgumentException("File may not be null");
    }
    this.file = file;
    this.listener = listener;
    this.transferredBytes = 0;
    setContentType(contentType);
  }

  public boolean isRepeatable()
  {
    return true;
  }

  public long getContentLength()
  {
    return this.file.length();
  }

  public InputStream getContent() throws IOException
  {
    return new FileInputStream(this.file);
  }

  public void writeTo(final OutputStream outstream) throws IOException
  {
    if (outstream == null)
    {
      throw new IllegalArgumentException("Output stream may not be null");
    }
    InputStream instream = new FileInputStream(this.file);
    try
    {
      byte[] tmp = new byte[4096];
      int l;
      while ((l = instream.read(tmp)) != -1)
      {
        outstream.write(tmp, 0, l);
        this.transferredBytes += l;
        this.listener.updateTransferred(this.transferredBytes);
      }
      outstream.flush();
    }
    finally
    {
      instream.close();
    }
  }

  public boolean isStreaming()
  {
    return false;
  }

  @Override
  public Object clone() throws CloneNotSupportedException
  {
    return super.clone();
  }
}

ProgressBarListener.java

import javax.swing.JProgressBar;

public class ProgressBarListener
{

  private int transferedMegaBytes = 0;
  private JProgressBar progressBar = null;

  public ProgressBarListener()
  {
    super();
  }

  public ProgressBarListener(JProgressBar progressBar)
  {
    this();
    this.progressBar = progressBar;
  }

  public void updateTransferred(long transferedBytes)
  {
    transferedMegaBytes = (int) (transferedBytes / 1048576);
    this.progressBar.setValue(transferedMegaBytes);
    this.progressBar.paint(progressBar.getGraphics());
    System.out.println("Transferred: " + transferedMegaBytes + " Megabytes.");
  }
}

Счастливое кодирование!

Ответ 2

Я представил производный FileEntity, который просто учитывает записанные байты. Он использует OutputStreamProgress, который делает фактический подсчет (вид декоратора для фактического OutputStream).

преимущество этого (и в целом украшения) заключается в том, что я не должен копировать фактическую реализацию, например, фактическое копирование из потока файлов в выходной поток. Я также могу изменить использование другой (более новой) реализации, например, NFileEntity.

Enjoy...

FileEntity.java

public class FileEntity extends org.apache.http.entity.FileEntity {

    private OutputStreamProgress outstream;

    public FileEntity(File file, String contentType) {
        super(file, contentType);
    }

    @Override
    public void writeTo(OutputStream outstream) throws IOException {
        this.outstream = new OutputStreamProgress(outstream);
        super.writeTo(this.outstream);
    }

    /**
     * Progress: 0-100
     */
    public int getProgress() {
        if (outstream == null) {
            return 0;
        }
        long contentLength = getContentLength();
        if (contentLength <= 0) { // Prevent division by zero and negative values
            return 0;
        }
        long writtenLength = outstream.getWrittenLength();
        return (int) (100*writtenLength/contentLength);
    }
}

OutputStreamProgress.java

public class OutputStreamProgress extends OutputStream {

    private final OutputStream outstream;
    private volatile long bytesWritten=0;

    public OutputStreamProgress(OutputStream outstream) {
        this.outstream = outstream;
    }

    @Override
    public void write(int b) throws IOException {
        outstream.write(b);
        bytesWritten++;
    }

    @Override
    public void write(byte[] b) throws IOException {
        outstream.write(b);
        bytesWritten += b.length;
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        outstream.write(b, off, len);
        bytesWritten += len;
    }

    @Override
    public void flush() throws IOException {
        outstream.flush();
    }

    @Override
    public void close() throws IOException {
        outstream.close();
    }

    public long getWrittenLength() {
        return bytesWritten;
    }
}

Ответ 3

Новая версия, использующая пакет org.apache.commons.io.output из commons-io (2.4) и его класс CountingOutputStream.

Я изменил исходный код, чтобы отразить, что моему проекту необходимо использовать многостраничную форму в качестве входного и почтового метода (это соответствует требованиям, предъявляемым на стороне сервера).

Учтите, что дельта большого файла соответствует моим испытаниям 4096 байт. Это означает, что метод listener counterChanged() вызывается каждые 4096 байт переданных данных, что приемлемо для моего варианта использования.

Метод выглядит так:

public void post(String url, File sendFile) {
    HttpParams params = new BasicHttpParams();
    params.setParameter(HttpProtocolParams.USE_EXPECT_CONTINUE, true);
    HttpProtocolParams.setVersion(params, HttpVersion.HTTP_1_1);
    HttpClient client = new DefaultHttpClient(params);
    HttpPost post = new HttpPost(url + "/" + sendFile.getName());
    MultipartEntity multiEntity = new MultipartEntity(); 
    MyFileBody fileBody = new MyFileBody(sendFile);

    fileBody.setListener(new IStreamListener(){

        @Override
        public void counterChanged(int delta) {
            // do something
            System.out.println(delta);
        }});

    multiEntity.addPart("file", fileBody);
    StringBody stringBody = new StringBody(sendFile.getName());
    multiEntity.addPart("fileName", stringBody);
    post.setEntity(multiEntity);   
    HttpResponse response = client.execute(post);
}

Класс MyFileBody будет выглядеть следующим образом:

public class MyFileBody extends FileBody {

    private IStreamListener listener;

    public MyFileBody(File file) {
        super(file);
    }

    @Override
    public void writeTo(OutputStream out) throws IOException {
        CountingOutputStream output = new CountingOutputStream(out) {
            @Override
            protected void beforeWrite(int n) {
                if (listener != null && n != 0)
                    listener.counterChanged(n);
                super.beforeWrite(n);
            }
        };
        super.writeTo(output);

    }

    public void setListener(IStreamListener listener) {
        this.listener = listener;
    }

    public IStreamListener getListener() {
        return listener;
    }

}

Наконец, интерфейс слушателя выглядит так:

public interface IStreamListener {

    void counterChanged(int delta);

}

Ответ 4

Этот ответ расширяет ответ на кикалу, добавляя простой слушатель к классу OutputStreamProgress.java вместо того, чтобы иметь общедоступный метод getProgress() (я честно не уверен, как вы предполагаете вызывать метод getProgress(), поскольку поток будет выполняйте внутри кода httpclient все время, которое вы, возможно, захотите вызвать getProgress()!).

Обратите внимание, что вам необходимо расширить класс сущности для каждого типа сущности, который вы хотите использовать, и когда вы пишете код HttpClient, вам нужно создать объект этого нового типа.

Я написал очень простой прослушиватель записи, который реализует интерфейс WriteListener. Здесь вы добавите свою логику, чтобы что-то сделать с отчетами о записи из OutputStreamProgress, что-то вроде обновления строки выполнения:)

Большое спасибо килаке за использование идеи декоратора, чтобы проникнуть в счетный поток.

WriteLisener.java

public interface WriteListener {
    void registerWrite(long amountOfBytesWritten);
}

OutputStreamProgress.java

import java.io.IOException;
import java.io.OutputStream;

public class OutputStreamProgress extends OutputStream {

    private final OutputStream outstream;
    private long bytesWritten=0;
    private final WriteListener writeListener;
    public OutputStreamProgress(OutputStream outstream, WriteListener writeListener) {
        this.outstream = outstream;
        this.writeListener = writeListener;
    }

    @Override
    public void write(int b) throws IOException {
        outstream.write(b);
        bytesWritten++;
        writeListener.registerWrite(bytesWritten);
    }

    @Override
    public void write(byte[] b) throws IOException {
        outstream.write(b);
        bytesWritten += b.length;
        writeListener.registerWrite(bytesWritten);
    }

    @Override
    public void write(byte[] b, int off, int len) throws IOException {
        outstream.write(b, off, len);
        bytesWritten += len;
        writeListener.registerWrite(bytesWritten);
    }

    @Override
    public void flush() throws IOException {
        outstream.flush();
    }

    @Override
    public void close() throws IOException {
        outstream.close();
    }
}

BasicWriteListener

public class BasicWriteListener implements WriteListener {

public BasicWriteListener() {
    // TODO Auto-generated constructor stub
}

public void registerWrite(long amountOfBytesWritten) {
    System.out.println(amountOfBytesWritten);
}

}

MultipartEntityWithProgressBar

import java.io.IOException;
import java.io.OutputStream;
import java.nio.charset.Charset;

import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;

public class MultipartEntityWithProgressBar extends MultipartEntity {
    private OutputStreamProgress outstream;
    private WriteListener writeListener;

    @Override
    public void writeTo(OutputStream outstream) throws IOException {
        this.outstream = new OutputStreamProgress(outstream, writeListener);
        super.writeTo(this.outstream);
    }

    public MultipartEntityWithProgressBar(WriteListener writeListener)
    {
        super();
        this.writeListener = writeListener;
    }
    public MultipartEntityWithProgressBar(HttpMultipartMode mode, WriteListener writeListener)
    {
        super(mode);
        this.writeListener = writeListener;
    }
    public MultipartEntityWithProgressBar(HttpMultipartMode mode, String boundary, Charset charset, WriteListener writeListener)
    {
        super(mode, boundary, charset);
        this.writeListener = writeListener;
    }

    // Left in for clarity to show where I took from kilaka answer
//  /**
//   * Progress: 0-100
//   */
//  public int getProgress() {
//      if (outstream == null) {
//          return 0;
//      }
//      long contentLength = getContentLength();
//      if (contentLength <= 0) { // Prevent division by zero and negative values
//          return 0;
//      }
//      long writtenLength = outstream.getWrittenLength();
//      return (int) (100*writtenLength/contentLength);
//  }

}