1

我正在为 Android 设备开发一个应用程序,其中一部分是启用用户 Google Docs 和设备存储之间的下载和上传。我遇到的问题是我在不同版本的 Android API 之间得到了不同的行为。我一直在 API lvl 10 (Android 2.3.3) 上进行大部分开发。虚拟设备上没有问题(我没有使用此 API lvl 或更高级别测试的真实设备)。在 API lvl 8 (2.2.x) 及以下的设备和模拟器上,当请求启动可恢复的上传会话时,我遇到了来自 Google Docs Api 的 411 Length required 错误。在 API lvl 10 的模拟器上运行相同的应用程序时不会发生这种情况。

我正在使用 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 请求。对于用户 Google Docs 的“根”,地址是“ 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 lvl 10 的模拟器:

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 lvl 7 的模拟器:

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.x 的设备),最好不使用特定于版本的代码。

老实说,我想不出可以在我的代码中应用的许多合适的解决方案。我唯一能想到的:

transport = new NetHttpTransport();
transport.defaultHeaders.contentLength = Integer.toString(0);

使用 API 中的(已弃用)方法以不同方式设置标头,但无济于事。请求中的实际标头仍然相同。

使用“0”或 Integer.toString(0) 设置属性也没有什么区别(显然,我在这里有点绝望)。

因此,非常欢迎任何旨在寻找解决方案的帮助或建议。如果特别要求,我将提供更多代码,并且可以通过数据包嗅探来测试不同的解决方案。这也很有可能是 Google Api Java 客户端或 Android 中的错误。但是哪一个?如果没有找到解决方案,我对 Android 的了解不够深入,无法弄清楚在哪里报告这个(疑似)错误。因此,如果您怀疑罪魁祸首确实不是我的代码,那么请分享您对哪个组件导致标题以不同方式设置的想法。

编辑 - 算法要求堆栈跟踪,这里是: http: //pastebin.com/yDCCLB2P

编辑 - 我正在为内容设置一个 mimetype,尽管正文是空的。我尝试删除内容类型标头,没有任何改进。该行现在在上面的代码中被注释掉了。

编辑 - 我一直在尝试解决这个问题。这是尝试在同一代码中以两种不同方式设置标头时的堆栈跟踪:http: //pastebin.com/NkmFjYB3

headers.contentLength = "0";
headers.set("Content-length", "0");

当同时使用时,它们会相互碰撞。删除任何一个都将产生与以前类似的结果(Docs 中需要 411 长度,并且在请求中不存在 content-length)。使用其中一种方式和不推荐使用的方式( transport.defaultHeaders.contentLength = "0"; )创建请求时,不会发生冲突(或不会出现在堆栈跟踪中)。使用上述任何方法的任意组合,要么存在双标头冲突,要么请求没有内容长度。

4

2 回答 2

3

改用这个:

HttpTransport transport = AndroidHttp.newCompatibleTransport();

这就是 Google 推荐的与所有 API 级别的兼容性,因为它会根据 API 的版本选择正确的实现。您不必自己实施。我已经对其进行了测试 - 适用于 2.2 和 2.3。

于 2011-07-19T16:44:41.680 回答
0

您可以尝试在 Android 环境中使用 ApacheHttpTransport 而不是 NetHttpTransport。NetHttpTransport 有几个问题,包括您在此处遇到的问题,尤其是 2.3 之前的版本。

根据javadocs:

从 SDK 2.3 开始,强烈建议使用 com.google.api.client.javanet.NetHttpTransport。他们的 Apache HTTP 客户端实现没有得到很好的维护。

对于 SDK 2.2 及更早版本,不建议使用 com.google.api.client.apache.ApacheHttpTransport com.google.api.client.javanet.NetHttpTransport,因为 Android SDK 实现 HttpURLConnection 存在一些错误。

我一直在 Android 2.3 环境中使用 ApacheHttpTransport,没有任何问题。如果您想支持多个 API 级别并希望遵循 javadocs 中的指南,您将需要实现某种能够根据您的代码运行的 API 级别提供正确传输的工厂。

于 2011-07-19T16:26:56.753 回答