安卓的HttpURLConnection:帖子多部分 [英] Android HttpUrlConnection: Post Multipart

查看:417
本文介绍了安卓的HttpURLConnection:帖子多部分的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想发布测试文件部署在采用Android tomcat的弹簧休息的servlet。我正在开发在Android 4.1.2,但我已经在4.0.3验证同样的问题。

I'm trying to post a test File to a spring rest servlet deployed on tomcat using Android. I'm developing on Android 4.1.2, but I have verified same problem on 4.0.3.

的问题是,在文件上载需要很长的时间(一个4MB文件约70秒),也是在本地网络。使用3G连接的时间是equiparable。我已经排除这可能是一个服务器的问题:用卷曲执行相同的调用也需要1/2秒,使用Apache作为后端的结果都是一样的。

The problem is that the file upload requires a very long time (about 70 seconds for a 4MB file), also in local network. The time is equiparable using a 3g connection. I've excluded that it could be a server problem: executing the same call with curl it takes 1 / 2 seconds, and using apache as backend results are the same.

使用的HttpClient 工作正常。

Using HttpClient works fine.

我使用的是春天的Andr​​oid RestClient 1.0.1.RELEASE,鉴于Android版本,事实上,我不是重写默认行为,它使用HttpURLConnection的,而不是HttpClient的,使HTTP请求。​​

I'm using Spring Android RestClient 1.0.1.RELEASE and, given Android version and the fact that I'm not overriding default behaviour, it uses HttpUrlConnection instead of HttpClient to make http requests.

我也实施了我的自定义ClientHtt prequestFactory以操纵SSL连接的一些细节,我定义我自己执行ClientHtt prequestInterceptor,以便修改认证头。

I have also implemented my custom ClientHttpRequestFactory in order to manipulate some details of SSL connection and I have defined my own implementation of ClientHttpRequestInterceptor in order to modify authentication header.

我还设置 setBufferRequestBody(假)为了避免 OutOfMemoryException异常上的大文件。但是,这个属性对所需时间没有影响。

I have also set setBufferRequestBody(false) in order to avoid OutOfMemoryException on big files. But this property have no effects on time required.

MyClientHtt prequestFactory

public class MyClientHttpRequestFactory extends SimpleClientHttpRequestFactory{

    @Override
    protected void prepareConnection(HttpURLConnection connection,  String httpMethod) throws IOException {
        super.prepareConnection(connection, httpMethod);
        connection.setConnectTimeout(240 * 1000);
        connection.setReadTimeout(240 * 1000);


        if ("post".equals(httpMethod.toLowerCase())) {
            setBufferRequestBody(false);
        }else {
            setBufferRequestBody(true);
        }
    }

@Override
protected HttpURLConnection openConnection(URL url, Proxy proxy) throws IOException {
    final HttpURLConnection httpUrlConnection = super.openConnection(url, proxy);

    if (url.getProtocol().toLowerCase().equals("https")
        &&
        settings.selfSignedCert().get())
    {
        try {
            ((HttpsURLConnection)httpUrlConnection).setSSLSocketFactory(getSSLSocketFactory());
            ((HttpsURLConnection)httpUrlConnection).setHostnameVerifier(new NullHostnameVerifier());
        } catch (Exception e) {
            MyLog.e(LOG_TAG, "OpenConnection", e);
        } 
    } 

    return httpUrlConnection;
}

MyClientHtt prequestInterceptor

public class MyClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {

    @Override
    public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {
        final HttpHeaders headers = request.getHeaders();

        headers.setAuthorization(new HttpBasicAuthentication( settings.username().get(), settings.password().get()));

        if (settings.enable_gzip().get()) {
            headers.setAcceptEncoding(ContentCodingType.GZIP);
        }

        return execution.execute(request, body);
    }
}

在这里,我的REST调用:

And here my Rest call:

List<ClientHttpRequestInterceptor> interceptors = Arrays.asList((ClientHttpRequestInterceptor)myClientHttpRequestInterceptor);

RestTemplate restTemplate = new RestTemplate();
restTemplate.getMessageConverters().add(new FormHttpMessageConverter());
restTemplate.getMessageConverters().add(new StringHttpMessageConverter());
restTemplate.getMessageConverters().add(new MappingJackson2HttpMessageConverter());
restTemplate.setInterceptors(interceptors);

MultiValueMap<String, Object> parts = new LinkedMultiValueMap<String, Object>();
parts.add("file", new FileSystemResource("/sdcard/test/4MB_file"));

HttpEntity<MultiValueMap> requestEntity = new HttpEntity<MultiValueMap>(parts);
restTemplate.exchange(myUrl, HttpMethod.POST, requestEntity, Integer.class).getBody();

}

看着春Android源$ C ​​$ C,$ C $的下一行C我的要求正在通过如下:

Looking at Spring Android source code, the next lines of code my request is passing through are:

public ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod) throws IOException {
    HttpURLConnection connection = openConnection(uri.toURL(), this.proxy);
    prepareConnection(connection, httpMethod.name());
    if (this.bufferRequestBody) {
        return new SimpleBufferingClientHttpRequest(connection);
    } else {
        return new SimpleStreamingClientHttpRequest(connection, this.chunkSize);
    }
}

由于 this.bufferRequestBody 返回新SimpleStreamingClientHtt prequest (连接,this.chunkSize); 执行(与CHUNKSIZE = 0)

Because of this.bufferRequestBody is false, return new SimpleStreamingClientHttpRequest(connection, this.chunkSize); is executed (with chunkSize = 0)

SimpleStreamingClientHttpRequest(HttpURLConnection connection, int chunkSize) {
    this.connection = connection;
    this.chunkSize = chunkSize;

    // Bugs with reusing connections in Android versions older than Froyo (2.2)
    if (olderThanFroyo) {
        System.setProperty("http.keepAlive", "false");
    }
}

然后:

ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), request.getMethod());

delegate.getHeaders().putAll(request.getHeaders());

if (body.length > 0) {
    FileCopyUtils.copy(body, delegate.getBody());
}
return delegate.execute();

从这里是所有的Andr​​oid子系统我想..

From here is all android subsystem I think..

我有倾倒的TCP流量,并分析它:

I have dumped tcp traffic and analyzed it:

POST /urlWherePost HTTP/1.1
Content-Type: multipart/form-data;boundary=nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Authorization: Basic xxxxxxxxxxxxxxxxxxxxxxxxxx=
Accept-Encoding: gzip
User-Agent: Dalvik/1.6.0 (Linux; U; Android 4.1.2; sdk Build/MASTER)
Host: 192.168.168.225:8080
Connection: Keep-Alive
Content-Length: 4096225

--nKwsP85ZyyzSDuAqozCTuZOSxwF1jLAtd0FECUPF
Content-Disposition: form-data; name="file"; filename="4MB_file"
Content-Type: application/octet-stream
Content-Length: 4096000

我trye​​d重新创建卷曲类似的要求:

I've tryed to re-create similar request with curl:

curl --verbose 
    -H "Connection: Keep-Alive" 
    -H "Content-Type: multipart/form-data" 
    -H "Accept-Encoding: gzip" 
    -H "Content-Disposition: form-data; name=\"file\"; filename=\"4MB_file\"" 
    -H "Content-Type: application/octet-stream" 
    --user xxx:xxx 
    -X POST 
    --form file=@4MB_file 
    http://192.168.168.225:8080/urlWherePost

但卷曲后是确定的。

but with curl the post is ok.

发帖JSON数据是没有问题的(也许小的机身尺寸)。但是,当我尝试发送大文件的时间的增加。

Posting json data is not a problem (maybe small body size). But when I try to send "big" files the time increase.

展望DDMS外壳,在网络统计,我还发现,网络吞吐量是永远不会超过250KB的德克萨斯州。似乎有一个bootleneck,但如何进行调查?我在哪里可以看,我可以更改参数?

Looking in DDMS shell, on Network Statistics I've also found that the network throughput is never over 250kb in TX. There seems to be a bootleneck, but how to investigate it? Where I can look, which parameter can I change?

感谢您的任何建议!

推荐答案

你有没有尝试过使用MultipartEntity方法?我从服务器下载的JSON数据量很大时,有同样的问题,但我切换到这个方法,抓住所有的服务器为我提供的数据。

Have you tried using the MultipartEntity method? I had the same problem when downloading big amounts of JSON data from the server, but I switched to this method and caught all the data that the server provided me.

HttpClient httpclient = new DefaultHttpClient();
HttpPost httppost = new HttpPost("http://myurl.com");

try {
    MultipartEntity entity = new MultipartEntity();

    entity.addPart("type", new StringBody("json"));
    entity.addPart("data", new JSONObject(data));
    httppost.setEntity(entity);
    HttpResponse response = httpclient.execute(httppost);
} catch (ClientProtocolException e) {
} catch (IOException e) {
} catch (JSONException e){
}

这篇关于安卓的HttpURLConnection:帖子多部分的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆