2012-07-20 23 views
7

私はジャージクライアントがあり、プログレスバーを必要とするほど大きなファイルをアップロードする必要があります。
問題は、アップロードが数分かかるため、アプリケーションが起動したらすぐに100%に移動することがわかります。その後、 "完了した"文字列を印刷するのに数分かかります。
バッファーに送られたかのようで、実際のアップロード速度であるの代わりにバッファー転送速度を読み取っていました。これはプログレスバーを役に立たなくします。ジャージクライアントのアップロードの進捗状況

これは非常に単純なコードです:私がいるContainerListenerフィルタを追加した、obviouslt builder.post呼び出す前に

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

進行状態を取得するには:

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

答えて

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

をあなたはまた、progressイベントを受信したときに転送されたファイルを認識するためにいくつかのアルゴリズムが含まれなければならないでしょう。

編集は:

私は単にデフォルトの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); 
+0

トマシュに感謝、この答えはとても良いです。クライアントを設定する2つの方法を提供してくれたという事実は、本当に立派で説明的です。不幸にも、問題は解決しません。私はentityStream.writeの後にSystem.out.println(...)を置くだけですが、その結果、大きなファイル(> 10MB)を数分の1秒で書き込んだ後、「本当の」アップロードが行われている間にフリーズします。この解決策でも起こる事実は、問題が他の場所にあることを意味します。あなたの答えは、私はそれを受け入れることができませんが、私は正しいとマークすることがうれしいでしょう別の特定の質問を開始することができます。 :-) – AgostinoX

+0

私はentityStream.flush()も追加しようと試みました。 entityStream.write(...)の後にバッファに書き込むのではなく、実際の書き込みを強制的にソケットに強制します。同じ結果:-( – AgostinoX

+0

ok、素晴らしい答え、それは両方の方法で、リスナーとカスタムファイルプロバイダで動作します。おそらく、ソリューションが2番目の部分であることを強調する必要があります。プロバイダーはリスナーの代わりとして興味深いですし、ジャージーアーキテクチャーを明確にするのに役立ちますので、私はそれを保つでしょうが、質問への直接的な答えとしてではありません。 – AgostinoX

3

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

} 

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

関連する問題