9

進行状況バーを必要とするのに十分な大きさのファイルをアップロードする必要があるジャージー クライアントがあります。
問題は、数分かかるアップロードの場合、アプリケーションが開始されるとすぐに転送されたバイト数が 100% になることです。次に、「on finished」文字列を出力するのに数分かかります。
バイトがバッファに送信されたかのようで、実際のアップロード速度ではなく、バッファへの転送速度を読み取っていました。これにより、進行状況バーが役に立たなくなります。

これは非常に単純なコードです:

ClientConfig config = new DefaultClientConfig();
Client client = Client.create(config);
WebResource resource = client.resource("www.myrestserver.com/uploads");
WebResource.Builder builder = resource.type(MediaType.MULTIPART_FORM_DATA_TYPE);

FormDataMultiPart multiPart = new FormDataMultiPart();
FileDataBodyPart fdbp = new FileDataBodyPart("data.zip", new File("data.zip"));
BodyPart bp = multiPart.bodyPart(fdbp);
String response = builder.post(String.class, multiPart);

進行状況を取得するために、builder.post を呼び出す前に ContainerListener フィルターを追加しました。

final ContainerListener containerListener = new ContainerListener() {

        @Override
        public void onSent(long delta, long bytes) {
            System.out.println(delta + " : " + long);
        }

        @Override
        public void onFinish() {
            super.onFinish();
            System.out.println("on finish");
        }

    };

    OnStartConnectionListener connectionListenerFactory = new OnStartConnectionListener() {
        @Override
        public ContainerListener onStart(ClientRequest cr) {
            return containerListener;
        }

    };

    resource.addFilter(new ConnectionListenerFilter(connectionListenerFactory));
4

3 に答える 3

4

Jersey 2.X では、WriterInterceptorを使用して、書き込みを追跡し、アップロードの進行状況コードを通知する Apache Commons IO CountingOutputStream のサブクラスで出力ストリームをラップしました (表示されていません)。

public class UploadMonitorInterceptor implements WriterInterceptor {

    @Override
    public void aroundWriteTo(WriterInterceptorContext context) throws IOException, WebApplicationException {

        // the original outputstream jersey writes with
        final OutputStream os = context.getOutputStream();

        // you can use Jersey's target/builder properties or 
        // special headers to set identifiers of the source of the stream
        // and other info needed for progress monitoring
        String id = (String) context.getProperty("id");
        long fileSize = (long) context.getProperty("fileSize");

        // subclass of counting stream which will notify my progress
        // indicators.
        context.setOutputStream(new MyCountingOutputStream(os, id, fileSize));

        // proceed with any other interceptors
        context.proceed();
    }

}

次に、このインターセプターをクライアント、またはインターセプターを使用する特定のターゲットに登録しました。

于 2015-07-10T17:41:35.457 に答える
3

java.io.File に独自の MessageBodyWriter を提供するだけで十分です

@Provider()
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public class MyFileProvider implements MessageBodyWriter<File> {

    public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return File.class.isAssignableFrom(type);
    }

    public void writeTo(File t, Class<?> type, Type genericType, Annotation annotations[], MediaType mediaType, MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws IOException {
        InputStream in = new FileInputStream(t);
        try {
            int read;
            final byte[] data = new byte[ReaderWriter.BUFFER_SIZE];
            while ((read = in.read(data)) != -1) {
                entityStream.write(data, 0, read);
                // fire some event as progress changes
            }
        } finally {
            in.close();
        }
    }

    @Override
    public long getSize(File t, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType) {
        return t.length();
    }
}

クライアント アプリケーションでこの新しいプロバイダーを使用するには、次のようにします。

ClientConfig config = new DefaultClientConfig();
config.getClasses().add(MyFileProvider.class);

また

ClientConfig config = new DefaultClientConfig();
MyFileProvider myProvider = new MyFileProvider ();
cc.getSingletons().add(myProvider);

進行状況イベントを受信したときに転送されるファイルを認識するためのアルゴリズムも含める必要があります。

編集:

デフォルトで HTTPUrlConnection がバッファリングを使用していることがわかりました。そして、バッファリングを無効にするには、いくつかのことを行うことができます:

  1. httpUrlConnection.setChunkedStreamingMode(chunklength) - バッファリングを無効にし、チャンク転送エンコーディングを使用してリクエストを送信します
  2. httpUrlConnection.setFixedLengthStreamingMode(contentLength) - バッファリングを無効にしますが、ストリーミングにはいくつかの制約があります: 正確なバイト数を送信する必要があります

したがって、問題の最終的な解決策は最初のオプションを使用し、次のようになることをお勧めします。

ClientConfig config = new DefaultClientConfig();
config.getClasses().add(MyFileProvider.class);
URLConnectionClientHandler clientHandler = new URLConnectionClientHandler(new HttpURLConnectionFactory() {
     @Override
     public HttpURLConnection getHttpURLConnection(URL url) throws IOException {
           HttpURLConnection connection = (HttpURLConnection) url.openConnection();
                connection.setChunkedStreamingMode(1024);
                return connection;
            }
});
Client client = new Client(clientHandler, config);
于 2012-07-20T20:21:46.070 に答える