2012-04-27 133 views
2

我试图得到一个Android应用使用文档列表API与谷歌文档/驱动互动:https://developers.google.com/google-apps/documents-list文档列表API文件上传失败时,文件大小比继续上传块大小更大

我在跑使用GData可恢复上传协议上传文件有点麻烦,即: https://developers.google.com/google-apps/documents-list#uploading_a_new_document_or_file_with_both_metadata_and_content

我正在使用512KiB块大小。小于块大小的文件成功上传,但大于块大小的文件将在第一块完成之前失败。即使将块大小增加到1MiB或2MiB,情况也是如此。一个768KiB文件将失败,块大小为512KiB,但成功的块大小为1MiB。

大于块大小的文件确实超过了第一步,即将XML张贴到“可恢复的创建媒体”链接。然后我看到用于发送块的HttpContent实现的writeTo()方法中抛出的SSLException。这通过异常FIRST块中途发生,例如:

javax.net.ssl.SSLException: Write error: ssl=0x2a6318: I/O error during system call, Broken pipe 
    at org.apache.harmony.xnet.provider.jsse.NativeCrypto.SSL_write(Native Method) 
    at org.apache.harmony.xnet.provider.jsse.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:713) 
    at libcore.net.http.FixedLengthOutputStream.write(FixedLengthOutputStream.java:41) 
    at com.google.api.client.http.AbstractInputStreamContent.writeTo(AbstractInputStreamContent.java:89) 

注意看上面的,我不得不实际拍摄的IOExceptions在我的writeTo()方法(登录后他们他们重新抛出)。否则,由于内容被突然中断,我只会看到一个通用的“400错误请求”响应。我已经尝试了几种不同的测试设备,例如Galaxy Nexus(VZW/CDMA,4.0.2,股票),Droid4(Moto 2.3.6,股票)DroidX(股票)。

页眉上的要求上传第一个块:

Accept-Encoding: gzip 
Authorization: GoogleLogin auth=XXXXXXXXXXXXXXXXXXXXXXXXXXXXX 
Content-Length: 524288 
Content-Range: 0-524287/1635085 
Content-Type: image/png 
GData-Version: 3.0 
User-Agent: XXXXXXXXXXXXXX 

感谢您的任何建议。

UPDATE:添加代码片段,这是PUT下一个块的位。

private boolean putNext() 
throws CancelException, DirectoryException { 
    try { 
     ByteArrayContent content = new ByteArrayContent(mediaType, buffer, 0, bufferLength); 

     HttpRequest request = conn.getHttpRequestFactory().buildPutRequest(new GenericUrl(nextLocation), content); 

     HttpHeaders requestHeaders = request.getHeaders(); 
     requestHeaders.setContentLength(String.valueOf(bufferLength)); 
     requestHeaders.setContentType(mediaType); 

     if (sent != 0 || bufferLength != size) { 
      // Only set content range when file will span multiple chunks. 
      requestHeaders.setContentRange(sent + "-" + (sent + bufferLength - 1) + "/" + size); 
     } 

     HttpResponse response = request.execute(); 

     sent += bufferLength; 
     bufferLength = 0; 

     HttpHeaders responseHeaders = response.getHeaders(); 

     int statusCode = response.getStatusCode(); 
     if (statusCode == GoogleDriveConnection.RESPONSE_308_RESUME_INCOMPLETE) { 
      nextLocation = responseHeaders.getLocation(); 
      return false; 
     } else if (statusCode >= 200 && statusCode < 400) { 
      Document responseDom = DomUtil.load(response.getContent()); 
      return true; 
     } else { 
      Log.w(LOG_TAG, "Google Drive upload error: Invalid response code to upload request: " + statusCode); 
      throw DirectoryException.networkErrorHost(null, catalog.getHostName()); 
     } 
    } catch (IOException ex) { 
     Log.w(LOG_TAG, "Google Drive write error.", ex); 
     throw DirectoryException.networkErrorHost(ex, catalog.getHostName()); 
    } catch (SAXException ex) { 
     throw DirectoryException.networkErrorHost(ex, catalog.getHostName()); 
    } 
} 

另一个可能很重要的注意事项:SSLException失败是由存在Content-Range标头触发的。如果我忽略它,那么第一块大于块大小的文件将成功上传,没有例外。服务器返回201 Created,并创建截断512KiB大小的文件。

再次感谢!

更新2

我现在有一个可用的纯Java应用程序来证明这个问题。这被打包成一个包含库的Eclipse项目,但应该在其他地方使用。 http://android.nextapp.com/test/DriveUpload.zip

验证令牌需要测试它。它需要三个命令行参数来运行:

[文件名] [内容/类型] [的authToken]

(我目前正在削减从调试输出在真正的应用程序/粘贴authtokens)

这里的此应用程序的修整输出:

TOKEN=******** 
FILE=/tmp/1199Panigale.jpg 
Headers: POST Request 
-- User-Agent: DriveUpload 
-- GData-Version: 3.0 
-- Authorization: GoogleLogin auth=******** 
-- X-Upload-Content-Type: image/png 
-- X-Upload-Content-Length: 1635085 
[POST Request] -------------------------------------------------------- 
<entry xmlns="http://www.w3.org/2005/Atom"> 
<title>1199Panigale.jpg</title> 
</entry> 
Executing post request: https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents?convert=false 
Post complete, response=200 
Headers: POST Response 
-- Server: HTTP Upload Server Built on Apr 23 2012 11:11:29 (1335204689) 
-- Location: https://docs.google.com/feeds/upload/create-session/default/private/full/folder%3Aroot/contents?convert=false&upload_id=********2 
-- Date: Sat, 28 Apr 2012 04:51:47 GMT 
-- Pragma: no-cache 
-- Expires: Fri, 01 Jan 1990 00:00:00 GMT 
-- Cache-Control: no-cache, no-store, must-revalidate 
-- Content-Length: 0 
-- Content-Type: text/html 
Preparing PUT request #0 
Headers: Put Request 
-- User-Agent: DriveUpload 
-- GData-Version: 3.0 
-- Authorization: GoogleLogin auth=******** 
-- Content-Type: image/png 
-- Content-Range: 0-524287/1635085 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 

[---skip a few---] 

Read: 4096, total: 270336 
Read: 4096, total: 274432 
Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 274432 
Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
Read: 4096, total: 290816 
Read: 4096, total: 294912 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: I/O exception (java.net.SocketException) caught when processing request: Broken pipe 
Apr 27, 2012 9:49:02 PM org.apache.http.impl.client.DefaultRequestDirector tryExecute 
INFO: Retrying request 
[writeTo] 
[getCotent] 
Read: 4096, total: 4096 
Read: 4096, total: 8192 
Read: 4096, total: 12288 
Read: 4096, total: 16384 
Read: 4096, total: 20480 

[---skip a few---] 

Read: 4096, total: 278528 
Read: 4096, total: 282624 
Read: 4096, total: 286720 
java.net.SocketException: Broken pipe 
    at java.net.SocketOutputStream.socketWrite0(Native Method) 
    at java.net.SocketOutputStream.socketWrite(SocketOutputStream.java:109) 
    at java.net.SocketOutputStream.write(SocketOutputStream.java:153) 
    at sun.security.ssl.OutputRecord.writeBuffer(OutputRecord.java:314) 
    at sun.security.ssl.OutputRecord.write(OutputRecord.java:303) 
    at sun.security.ssl.SSLSocketImpl.writeRecordInternal(SSLSocketImpl.java:768) 
    at sun.security.ssl.SSLSocketImpl.writeRecord(SSLSocketImpl.java:756) 
    at sun.security.ssl.AppOutputStream.write(AppOutputStream.java:108) 
    at org.apache.http.impl.io.AbstractSessionOutputBuffer.write(AbstractSessionOutputBuffer.java:153) 
    at org.apache.http.impl.io.ContentLengthOutputStream.write(ContentLengthOutputStream.java:114) 
    at driveupload.UploadStream$1.writeTo(UploadStream.java:149) 
    at org.apache.http.entity.HttpEntityWrapper.writeTo(HttpEntityWrapper.java:96) 
    at org.apache.http.impl.client.EntityEnclosingRequestWrapper$EntityWrapper.writeTo(EntityEnclosingRequestWrapper.java:108) 
    at org.apache.http.impl.entity.EntitySerializer.serialize(EntitySerializer.java:120) 
    at org.apache.http.impl.AbstractHttpClientConnection.sendRequestEntity(AbstractHttpClientConnection.java:264) 
    at org.apache.http.impl.conn.AbstractClientConnAdapter.sendRequestEntity(AbstractClientConnAdapter.java:224) 
    at org.apache.http.protocol.HttpRequestExecutor.doSendRequest(HttpRequestExecutor.java:255) 
    at org.apache.http.protocol.HttpRequestExecutor.execute(HttpRequestExecutor.java:123) 
    at org.apache.http.impl.client.DefaultRequestDirector.tryExecute(DefaultRequestDirector.java:647) 
    at org.apache.http.impl.client.DefaultRequestDirector.execute(DefaultRequestDirector.java:464) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:820) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:754) 
    at org.apache.http.impl.client.AbstractHttpClient.execute(AbstractHttpClient.java:732) 
    at driveupload.DriveUpload.executeRequest(DriveUpload.java:71) 
    at driveupload.UploadStream.putNext(UploadStream.java:191) 
    at driveupload.UploadStream.write(UploadStream.java:225) 
    at java.io.OutputStream.write(OutputStream.java:116) 
    at driveupload.DriveUpload.test(DriveUpload.java:127) 
    at driveupload.DriveUpload.main(DriveUpload.java:44) 
+0

您可以与我们分享您的代码中可恢复上传的部分吗?另外,看看这个[回答](http://stackoverflow.com/a/10195674/1106381),它显示了如何使用gdata-java-client库1.4.7版插入(或更新)一个新文件使用文档列表API。 – Alain 2012-04-27 15:49:12

+0

@Alain,谢谢,添加了一段代码和一些更多的信息,请参阅编辑问题。我实际上并没有在其网站上使用gdata-java-client per语句,它不再被主动维护。 – tliebeck 2012-04-27 21:08:27

+0

感谢您的代码片段,我会看看,并试图看看我能否找出问题来自哪里。此外,您是否可以确保在启动[可恢复上传请求](http://goo.gl/HrMNk)时指定了“X-Upload-Content-Length”标题?最后,您可以查看[ResumableHttpUloadTask.upload](http://code.google.com/p/gdata-java-client/source/browse/trunk/java/src/com/google/gdata/client /uploader/ResumableHttpUploadTask.java#191)方法来查看它是如何在我们的库中实现的(它仍然有一些发展:))。 – Alain 2012-04-27 21:37:15

回答

2

的问题原来是,我是无法前缀“字节”的“内容范围”标头。我不知道为什么我在前六次查找标题时都没有发现这一点,因为它与规范保持一致。 :)

感谢您的帮助,并抱歉打扰您与这个非问题。

虽然我注意到规范指出对PUT请求的响应将为下一次上载提供“位置”标头。我没有看到,但如果我坚持以前发布的位置,它工作正常。我的代码现在只需更新位置URL(如果提供了该位置URL),并继续以前发布的位置URL(如果没有)。