安卓网络框架,上传图片花图,上传状态411被服务器驳回
2015-10-25 15:48
956 查看
先看下一开始使用的网络框架核心代码:
很简单能看出,这是用的自己拼接http header和body,然后通过安卓原生最基础的网络类HttpURLConnection或DefaultHttpClient这之类的来实现。后来发现上传图片的时候总会出现图片颜色混乱花图、图片出现半张图等现象。于是做了如下改动:
至此,使用的是httpmime-4.1.3.jar包中的MultipartEntity然后通过HttpPost来进行上传,用了框架之后的好处是基本上遇不到上传图片变花的问题了,但半张图的问题仍然存在,最关键的是引入了上传图片文件服务器返回诡异的411错误,仅少部分手机会出现,而且是必然。这个在stackoverflow也有提出,不过一直没啥好的解法 http://stackoverflow.com/questions/15552276/answer/submit
进过测试发现,普通web服务器是完全没问题的,但我服务器是通过负载均衡来进行了一次转发,大概也就是在这个过程丢失了contentLength,系统也没按照content chunk读取不定长请求。第一反应是手动设置length参数,显然是错的了。
进过分析,很可能是inputStream流长度无法读取,所以contentLength消失。另外直接用流上传可能存在流中数据中断或异常,导致图片变画或只有一半。后来进一步研究才发现了MultipartEntity已经不提倡使用了,在新版的httpmime中要使用MultipartEntityBuilder。详见:http://www.2cto.com/kf/201402/276505.html
修改如下:
注意这里用的是addBinaryBody的byte[]方法,而不是直接传inputStream,果然content-length属性已经明显出来了,但此种方法用的自然不是content chunk读取不定长的方式。不过至此网络底层的问题也基本解决啦。留下来给自己做个历史。
private Message doPost(final String url, final Map<String, String> params, final Map<String, InputStream> inputStreams,final InterCallback callback) { long startTime = System.currentTimeMillis(); Message message=new Message(); String BOUNDARY = java.util.UUID.randomUUID().toString(); String PREFIX = "--", LINEND = "\r\n"; String CHARSET = XHConf.in().net_encode; HttpURLConnection conn=null; try { // if(url.indexOf("Upload/imgs")>-1) Thread.sleep(130*1000); URL uri = new URL(url); conn = (HttpURLConnection) uri.openConnection(); conn.setDoInput(true);// 允许输入 conn.setDoOutput(true);// 允许输出 conn.setUseCaches(false); conn.setRequestMethod("POST"); // Post方式 conn.setConnectTimeout(XHConf.in().net_timeout*2); conn.setReadTimeout(XHConf.in().net_timeout * 10); //设置header Map<String,String> header = callback.getReqHeader(new HashMap<String, String>(),url,params); UtilLog.print(XHConf.in().log_tag_net,"d","------------------REQ_POST------------------\n"+url+"\n"+params+";"+inputStreams+"\nheader:"+header.toString()); for (Map.Entry<String, String> map : header.entrySet()) { conn.setRequestProperty(map.getKey(), map.getValue()); } // 首先组拼文本类型的参数 StringBuilder sb = new StringBuilder(); if(inputStreams.isEmpty()){ for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(entry.getKey() + "=" + URLEncoder.encode(entry.getValue(),XHConf.in().net_encode) + "&"); } if(sb.length() > 1) sb.deleteCharAt(sb.length()-1); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); }else{ for (Map.Entry<String, String> entry : params.entrySet()) { sb.append(PREFIX); sb.append(BOUNDARY); sb.append(LINEND); sb.append("Content-Disposition: form-data; name=\"" + entry.getKey() + "\"" + LINEND); sb.append("Content-Type: text/plain; charset=" + CHARSET + LINEND); sb.append("Content-Transfer-Encoding: 8bit" + LINEND); sb.append(LINEND); sb.append(entry.getValue()); sb.append(LINEND); } conn.setRequestProperty("Content-Type", "multipart/form-data" + ";boundary=" + BOUNDARY); } DataOutputStream outStream = new DataOutputStream(conn.getOutputStream()); outStream.write(sb.toString().getBytes()); // 发送文件数据 if (!inputStreams.isEmpty()){ for (Map.Entry<String, InputStream> iss : inputStreams.entrySet()) { StringBuilder sb1 = new StringBuilder(); sb1.append(PREFIX); sb1.append(BOUNDARY); sb1.append(LINEND); //组装文件流数据 String[] fileNameSplit = iss.getKey().split("_"); String contentType = fileNameSplit[fileNameSplit.length - 1]; StringBuilder fileName = new StringBuilder(); if(fileNameSplit.length<3) throw new Exception("文件流的key用_无法切割:"+iss.getKey()); for(int i = 1; i < fileNameSplit.length - 1; i++){ fileName.append(fileNameSplit[i]); } sb1.append("Content-Disposition: form-data; name=\"" + fileNameSplit[0] + "[]\"; filename=\"" + fileName.toString() + "\"" + LINEND); sb1.append("Content-Type: "+contentType+"; charset=" + CHARSET + LINEND); sb1.append(LINEND); outStream.write(sb1.toString().getBytes()); InputStream is = iss.getValue(); byte[] buffer = new byte[1024]; int len = 0; while (is != null && (len = is.read(buffer)) != -1) { outStream.write(buffer, 0, len); } outStream.write(LINEND.getBytes()); } // 请求结束标志 byte[] end_data = (PREFIX + BOUNDARY + PREFIX + LINEND).getBytes(); outStream.write(end_data); } outStream.flush(); outStream.close(); // 得到响应 int resState = conn.getResponseCode(); if (resState == 200) { Map<String, List<String>> ml = conn.getHeaderFields(); if(ml.containsKey("Set-Cookie")){ Map<String,String> map = getPostCookieMap(ml.get("Set-Cookie")); callback.saveCookie(map,url,"doPost"); } message.what=REQ_OK_STRING; message.obj=UtilString.inputStream2String(conn.getInputStream(), XHConf.in().net_encode); } else { message.what=REQ_STATE_ERROR; message.obj=resState; } } catch (Exception e) { message.what=REQ_EXP; message.obj=e; } callback.requestTime=System.currentTimeMillis()-startTime; if(conn!=null) conn.disconnect(); return message; }
很简单能看出,这是用的自己拼接http header和body,然后通过安卓原生最基础的网络类HttpURLConnection或DefaultHttpClient这之类的来实现。后来发现上传图片的时候总会出现图片颜色混乱花图、图片出现半张图等现象。于是做了如下改动:
private Message doPost(final String url, final Map<String, String> params, final Map<String, InputStream> inputStreams,final InterCallback callback) { long startTime = System.currentTimeMillis(); Message message=new Message(); //准备链接 HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, XHConf.in().net_timeout*2); HttpConnectionParams.setSoTimeout(httpParameters, XHConf.in().net_timeout*10); DefaultHttpClient client = new DefaultHttpClient(httpParameters); HttpPost post = new HttpPost(url); Map<String,String> header = callback.getReqHeader(new HashMap<String, String>(),url,params); for (Map.Entry<String, String> map : header.entrySet()) { post.setHeader(map.getKey(), map.getValue()); } UtilLog.print(XHConf.in().log_tag_net,"d","------------------REQ_POST------------------\n"+url+"\n"+params+";"+inputStreams+"\nheader:"+header.toString()); try { //设置参数 if(inputStreams.isEmpty()){ ArrayList<NameValuePair> postEntry=new ArrayList<NameValuePair>(); for (Map.Entry<String, String> entry : params.entrySet()) { postEntry.add(new BasicNameValuePair(entry.getKey(),entry.getValue())); } post.setEntity(new UrlEncodedFormEntity(postEntry,XHConf.in().net_encode)); }else{ MultipartEntity multipartEntity = new MultipartEntity(); for (Map.Entry<String, String> entry : params.entrySet()) { StringBody sb = new StringBody(entry.getValue(),Charset.forName(XHConf.in().net_encode)); multipartEntity.addPart(entry.getKey(), sb); } for (Map.Entry<String, InputStream> entry : inputStreams.entrySet()) { //组装文件流数据 String[] fileNameSplit = entry.getKey().split("_"); // String contentType = fileNameSplit[fileNameSplit.length - 1]; StringBuilder fileName = new StringBuilder(); if(fileNameSplit.length<3) throw new Exception("文件流的key用_无法切割:"+entry.getKey()); for(int i = 1; i < fileNameSplit.length - 1; i++){ fileName.append(fileNameSplit[i]); } InputStreamBody isb = new InputStreamBody(entry.getValue(), fileName.toString()); multipartEntity.addPart(fileNameSplit[0] + "[]", isb); } post.setEntity(multipartEntity); } HttpResponse response = client.execute(post); int resState=response.getStatusLine().getStatusCode(); if (resState == HttpURLConnection.HTTP_OK) { message.what=REQ_OK_STRING; message.obj=UtilString.inputStream2String(response.getEntity().getContent(), XHConf.in().net_encode); callback.saveCookie(getGetCookieMap(client.getCookieStore().getCookies()),url,"doPost"); } else { message.what=REQ_STATE_ERROR; message.obj=resState; } } catch (Exception e) { message.what=REQ_EXP; message.obj=e; } callback.requestTime=System.currentTimeMillis()-startTime; client.getConnectionManager().shutdown(); return message; }
至此,使用的是httpmime-4.1.3.jar包中的MultipartEntity然后通过HttpPost来进行上传,用了框架之后的好处是基本上遇不到上传图片变花的问题了,但半张图的问题仍然存在,最关键的是引入了上传图片文件服务器返回诡异的411错误,仅少部分手机会出现,而且是必然。这个在stackoverflow也有提出,不过一直没啥好的解法 http://stackoverflow.com/questions/15552276/answer/submit
进过测试发现,普通web服务器是完全没问题的,但我服务器是通过负载均衡来进行了一次转发,大概也就是在这个过程丢失了contentLength,系统也没按照content chunk读取不定长请求。第一反应是手动设置length参数,显然是错的了。
进过分析,很可能是inputStream流长度无法读取,所以contentLength消失。另外直接用流上传可能存在流中数据中断或异常,导致图片变画或只有一半。后来进一步研究才发现了MultipartEntity已经不提倡使用了,在新版的httpmime中要使用MultipartEntityBuilder。详见:http://www.2cto.com/kf/201402/276505.html
修改如下:
private Message doPost(String url, Map<String, String> params, Map<String, byte[]> byteMap, InterCallback callback) { long startTime = System.currentTimeMillis(); Message message=new Message(); //准备连接 HttpParams httpParameters = new BasicHttpParams(); HttpConnectionParams.setConnectionTimeout(httpParameters, XHConf.in().net_timeout*2); HttpConnectionParams.setSoTimeout(httpParameters, XHConf.in().net_timeout*10); DefaultHttpClient client = new DefaultHttpClient(httpParameters); Map<String,String> header = callback.getReqHeader(new HashMap<String, String>(),url,params); header=changeHeader(url,header); UtilLog.print(XHConf.in().log_tag_net,"d","------------------REQ_POST------------------\n"+url+"\n"+params+";"+byteMap+"\nheader:"+header.toString()); HttpPost post = new HttpPost(changeUrlFromHeader(url, header)); for (Map.Entry<String, String> map : header.entrySet()) { post.addHeader(map.getKey(), map.getValue()); } try { //设置参数 if(byteMap.isEmpty()){ ArrayList<NameValuePair> postEntry=new ArrayList<NameValuePair>(); for (Map.Entry<String, String> entry : params.entrySet()) { postEntry.add(new BasicNameValuePair(entry.getKey(),entry.getValue())); } post.setEntity(new UrlEncodedFormEntity(postEntry,XHConf.in().net_encode)); }else{ Charset charset=Charset.forName(XHConf.in().net_encode); MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); multipartEntityBuilder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE); multipartEntityBuilder.setCharset(charset); for (Map.Entry<String, String> entry : params.entrySet()) { multipartEntityBuilder.addTextBody(entry.getKey(),entry.getValue(),ContentType.create("text/plain",charset)); } for (Map.Entry<String, byte[]> entry : byteMap.entrySet()) { //组装文件流数据 String[] fileNameSplit = entry.getKey().split("_"); String contentType = fileNameSplit[fileNameSplit.length - 1]; StringBuilder fileName = new StringBuilder(); if(fileNameSplit.length<3) throw new Exception("文件流的key用_无法切割:"+entry.getKey()); for(int i = 1; i < fileNameSplit.length - 1; i++){ fileName.append(fileNameSplit[i]); } multipartEntityBuilder.addBinaryBody(fileNameSplit[0] + "[]", entry.getValue(), ContentType.create(contentType), fileName.toString()); } post.setEntity(multipartEntityBuilder.build()); } HttpResponse response = client.execute(post); int resState=response.getStatusLine().getStatusCode(); if (resState == HttpURLConnection.HTTP_OK) { message.what=REQ_OK_STRING; message.obj=UtilString.inputStream2String(response.getEntity().getContent(), XHConf.in().net_encode); callback.saveCookie(getGetCookieMap(client.getCookieStore().getCookies()),url,"doPost"); } else { message.what=REQ_STATE_ERROR; message.obj=resState; } } catch (Exception e) { message.what=REQ_EXP; message.obj=e; } callback.requestTime=System.currentTimeMillis()-startTime; client.getConnectionManager().shutdown(); return message; }
注意这里用的是addBinaryBody的byte[]方法,而不是直接传inputStream,果然content-length属性已经明显出来了,但此种方法用的自然不是content chunk读取不定长的方式。不过至此网络底层的问题也基本解决啦。留下来给自己做个历史。
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories