2012-07-20 95 views
7

我有一個球衣客戶端,需要上傳足夠大的文件來需要進度條。
問題是,對於需要幾分鐘的上傳,只要應用程序啓動了,我就會看到轉移的字節數爲100%。然後打印「完成」字符串需要幾分鐘的時間。
就好像字節被髮送到緩衝區,並且我正在讀取轉移到緩衝區的速度而不是實際的上傳速度。這使進度條無用。
Jersey客戶端上傳進度

這是非常簡單的代碼:要獲得進步狀態我已經添加了過濾器的ContainerListener

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

,obviouslt之前調用builder.post:

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

你將不得不還包括一些算法來識別哪個文件轉移接收進度事件時。

編輯:

我剛剛發現,在默認情況下使用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

感謝Tomasz,這個答案非常好。您已經提供了兩種配置客戶端的方式,這一點非常令人敬佩和具有說明性。不幸的是,問題依然存在。我在entityStream.write之後放了一個System.out.println(...),但結果是我在幾秒鐘內寫入了大文件(> 10MB),然後在「真正」上傳發生時凍結。這個解決方案也會發生,這意味着問題在別處。對於你的答案,我不能接受它,但我可以開始另一個具體的問題,我很樂意將它標記爲正確的。 :-) – AgostinoX 2012-07-21 13:34:38

+0

我也嘗試添加一個entityStream.flush();在entityStream.write(...)之後,爲了強制實際寫入套接字而不是僅寫入緩衝區。同樣的結果:-( – AgostinoX 2012-07-21 13:45:22

+0

好的,很好的答案,它在兩方面起作用,即與聽衆和自定義文件提供者有關,也許應該強調的是,解決方案是第二部分,可能是將它移動到頂部。提供者作爲聽衆的替代品很有趣,而且它有助於澄清球衣架構,所以我會保留它,但不能作爲問題的直接答案。 – AgostinoX 2012-07-22 18:14:08

3

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

} 

然後,我將這個攔截器註冊到客戶端,或者在特定的目標上使用攔截器。