我正在为Android设备开发一个应用程序,其中一部分是允许用户在Google Docs和设备存储之间下载和上传。我遇到的问题是不同版本的Android API会有不同的行为。我一直在API ll10 (Android 2.3.3)上进行大部分开发。在虚拟设备上没有问题(我没有一个真正的设备来测试这个API级别或更高)。在API lvl 8 (2.2.x)及以下的设备和模拟器上,当请求启动可恢复的上传会话时,我遇到了来自Google Docs API的411长度要求错误。在API ll10的模拟器上运行相同的应用程序时不会发生这种情况。
我正在使用Eclipse进行开发,并使用Google API Java Client 1.4.1-beta与Docs API进行通信。我所遵循的Google Docs API的文档位于这里:http://code.google.com/apis/documents/docs/3.0/developers_guide_protocol.html
根据上述文档,要启动可恢复的上传会话,首先要发送一个空的POST请求。对于用户的"根"谷歌文档的地址是"https://docs.google.com/feeds/upload/create-session/default/private/full"。以下是我为(空体)请求设置报头的方式:
GoogleHeaders headers = new GoogleHeaders();
headers.contentLength = "0";
headers.gdataVersion = "3";
headers.setGoogleLogin(authToken);
//headers.contentType = getMimeType(file);
headers.setSlugFromFileName(file.getName());
headers.setApplicationName("test");
headers.set("X-Upload-Content-Length", file.length());
headers.set("X-Upload-Content-Type", getMimeType(file));
request.headers = headers;
我还应该提到我为HTTP使用的库如下:
com.google.api.client.googleapis.GoogleHeaders;
com.google.api.client.http.HttpRequest;
com.google.api.client.http.HttpRequestFactory;
com.google.api.client.http.HttpRequestInitializer;
com.google.api.client.http.HttpTransport;
com.google.api.client.http.HttpResponse;
com.google.api.client.http.HttpContent;
com.google.api.client.http.javanet.NetHttpTransport;
我做了一些数据包嗅探实际看到的头,你瞧,在不同版本的Android头实际上是不一样的设置方式。如果您注意到,默认情况下请求应该使用https发送,因此我将其更改为使用http来查看数据包的标头。以下是结果:
使用Android API模拟器:
POST /feeds/upload/create-session/default/private/full?convert=false HTTP/1.1
Accept-Encoding: gzip
Authorization: **REMOVED**
Content-Length: 0
Content-Type: text/plain
GData-Version: 3
Slug: 5mbfile.txt
User-Agent: test Google-API-Java-Client/1.4.1-beta
X-Upload-Content-Length: 5242880
X-Upload-Content-Type: text/plain
Host: docs.google.com
Connection: Keep-Alive
使用API的模拟器:
POST /feeds/upload/create-session/default/private/full?convert=false HTTP/1.1
accept-encoding: gzip
authorization: **REMOVED**
content-type: text/plain
gdata-version: 3
slug: 5mbfile.txt
user-agent: test Google-API-Java-Client/1.4.1-beta
x-upload-content-length: 5242880
x-upload-content-type: text/plain
Host: docs.google.com
Connection: Keep-Alive
注意缺少内容长度的标头,情况也有所不同。这解释了为什么我得到411响应,但如何解决这个问题?显然,我的目标是在所有设备上获得相同的行为(不包括Android 1)。(原因与此问题无关),最好不要使用特定版本的代码。
老实说,我想不出很多合适的解决方案可以应用到我的代码中。我唯一能想到的是:
transport = new NetHttpTransport();
transport.defaultHeaders.contentLength = Integer.toString(0);
在API中使用(已弃用的)方法设置不同的头,但无济于事。请求中的实际标头仍然是相同的。
将属性设置为"0"或Integer.toString(0)也没有区别(显然,我在这里有点绝望)。
所以任何旨在找到解决方案的帮助或建议都是非常受欢迎的。如果特别需要,我将提供更多的代码,并且可以通过数据包嗅探来测试不同的解决方案。这也很有可能是Google Api Java Client或Android中的一个bug。但是哪一个呢?如果没有找到解决方案,我对Android的了解不够深入,无法找出在哪里报告这个(可疑的)bug。因此,如果您怀疑罪魁祸首确实不是我的代码,那么请分享您对哪个组件导致以不同方式设置头的想法。
编辑-算法要求堆栈跟踪,这里是:http://pastebin.com/yDCCLB2P
编辑-我正在设置内容的mime类型,虽然正文是空的。我试着删除内容类型的标题,没有改进。在上面的代码中,这一行现在被注释掉了。
编辑-我一直在尝试解决这个问题。下面是在同一代码中以两种不同的方式设置头文件时的堆栈跟踪:http://pastebin.com/NkmFjYB3
headers.contentLength = "0";
headers.set("Content-length", "0");
同时使用时,它们会相互碰撞。删除其中任何一个都将产生与以前类似的结果(411 Length Required from Docs,并且在请求中没有content-length)。当使用其中一种和已弃用的方式(transport.defaultHeaders.contentLength = "0";)时,不会发生碰撞(或不会显示在堆栈跟踪中)。创建请求。对于上述任何方法的任何组合,要么存在双报头冲突,要么请求没有内容长度。
使用此相反:
HttpTransport transport = AndroidHttp.newCompatibleTransport();
这是Google为所有API级别的兼容性所推荐的,因为它会根据API的版本选择正确的实现。你不需要自己实现它。
你可以尝试在Android环境中使用ApacheHttpTransport而不是NetHttpTransport。NetHttpTransport有几个问题,包括你在这里遇到的问题,特别是在2.3之前。
根据javadocs:
从SDK 2.3开始,强烈建议使用com.google.api.client.javanet.NetHttpTransport。他们的Apache HTTP Client实现没有得到很好的维护。
对于SDK 2.2及更早版本,请使用com.google.api.client.apache.ApacheHttpTransport。com.google.api.client.javanet.NetHttpTransport不建议使用,因为在Android SDK中实现的HttpURLConnection存在一些bug。
我一直在Android 2.3环境中使用ApacheHttpTransport没有任何问题。如果你想支持多个API级别,并且想要遵循javadocs中的指导方针,你需要实现某种工厂,能够根据你的代码运行的API级别提供正确的传输。