轻松把玩HttpClient之封装HttpClient工具类(九),添加多文件上传功能

原创 2016年11月07日 14:13:39

       在Git上有人给我提Issue,说怎么上传文件,其实我一开始就想上这个功能,不过这半年比较忙,所以一直耽搁了。这次正好没什么任务了,赶紧完成这个功能。毕竟作为一款工具类,有基本的请求和下载功能,就差上传了,有点说不过去。好了,天不早了,咱干点正事吧。

       如果你只想了解怎么用HttpClient来上传文件,可以参考这篇文章:http://blog.csdn.net/fengyuzhengfan/article/details/39941851,里面写的很清楚了。这里我主要介绍工具类中的修改。

       首先,上传功能需要用到HttpMime这个,所以首先在pom中添加maven依赖(不会的自行百度)。

       其次,修改Utils中的map2List方法。阅读过源码的都知道,我所有的请求参数,都是通过map来封装的,原来都是返回list,上次升级以后,返回的都是HttpEntity对象(看来下次得修改一下方法名了map2HttpEntity尴尬),把所有的参数转化为HttpEntity对象。

	//装填参数
	HttpEntity entity = Utils.map2List(nvps, config.map(), config.inenc());
	//设置参数到请求对象中
	((HttpEntityEnclosingRequestBase)request).setEntity(entity);

       现在上传用到的是MultipartEntityBuilder,所以添加一个特定类型,进行特殊处理。

	public static final String ENTITY_MULTIPART="$ENTITY_MULTIPART$";
	private static final List<String> SPECIAL_ENTITIY = Arrays.asList(ENTITY_STRING, ENTITY_BYTES, ENTITY_FILE, ENTITY_INPUTSTREAM, ENTITY_SERIALIZABLE, ENTITY_MULTIPART);

	/**
	 * 参数转换,将map中的参数,转到参数列表中
	 * 
	 * @param nvps				参数列表
	 * @param map				参数列表(map)
	 * @throws UnsupportedEncodingException 
	 */
	public static HttpEntity map2List(List<NameValuePair> nvps, Map<String, Object> map, String encoding) throws UnsupportedEncodingException {
		HttpEntity entity = null;
		if(map!=null && map.size()>0){
			boolean isSpecial = false;
			// 拼接参数
			for (Entry<String, Object> entry : map.entrySet()) {
				if(SPECIAL_ENTITIY.contains(entry.getKey())){//判断是否在之中
					isSpecial = true;
					if(ENTITY_STRING.equals(entry.getKey())){//string
						entity = new StringEntity(String.valueOf(entry.getValue()), encoding);
						break;
					}else if(ENTITY_BYTES.equals(entry.getKey())){//file
						entity = new ByteArrayEntity((byte[])entry.getValue());
						break;
					}else if(ENTITY_FILE.equals(entry.getKey())){//file
						//....
						break;
					}else if(ENTITY_INPUTSTREAM.equals(entry.getKey())){//inputstream
//						entity = new InputStreamEntity();
						break;
					}else if(ENTITY_SERIALIZABLE.equals(entry.getKey())){//serializeable
//						entity = new SerializableEntity()
						break;
					}else if(ENTITY_MULTIPART.equals(entry.getKey())){//MultipartEntityBuilder
						File[] files  = null;
						if(File.class.isAssignableFrom(entry.getValue().getClass().getComponentType())){
							files=(File[])entry.getValue();
						}else if(entry.getValue().getClass().getComponentType()==String.class){
							String[] names = (String[]) entry.getValue();
							files = new File[names.length];
							for (int i = 0; i < names.length; i++) {
								files[i] = new File(names[i]);
							}
						}
						MultipartEntityBuilder builder = MultipartEntityBuilder.create();
						builder.setCharset(Charset.forName(encoding));// 设置请求的编码格式
						builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);// 设置浏览器兼容模式
						int count = 0;
						for (File file : files) {
							builder.addBinaryBody(String.valueOf(map.get(ENTITY_MULTIPART+".name")) + count++,file);
						}
						boolean forceRemoveContentTypeCharset = (Boolean)map.get(ENTITY_MULTIPART+".rmCharset");
						Map<String, Object> m = new HashMap<String, Object>();
						m.putAll(map);
						m.remove(ENTITY_MULTIPART);
						m.remove(ENTITY_MULTIPART+".name");
						m.remove(ENTITY_MULTIPART+".rmCharset");
						Iterator<Entry<String, Object>> iterator = m.entrySet().iterator();
						// 发送的数据
				        while (iterator.hasNext()) {
							Entry<String, Object> e = iterator.next();
				            builder.addTextBody(e.getKey(), String.valueOf(e.getValue()), ContentType.create("text/plain", encoding));
				        }
						entity = builder.build();// 生成 HTTP POST 实体
						
						//强制去除contentType中的编码设置,否则,在某些情况下会导致上传失败
						if(forceRemoveContentTypeCharset){
							removeContentTypeChraset(encoding, entity);
						}
						break;
					}else {
						nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
					}
				}else{
					nvps.add(new BasicNameValuePair(entry.getKey(), String.valueOf(entry.getValue())));
				}
			}
			if(!isSpecial) {
				entity = new UrlEncodedFormEntity(nvps, encoding);
			}
		}
		return entity;
	}

       这里面有一个方法removeContentTypeChraset,主要是为了解决,如果调用了setCharset,中文文件名不会乱码,但是在ContentType文件头中会多一个charset=xxx,而导致上传失败,解决办法就是强制去掉这个信息。而这个HttpEntity实际对象是MultipartFormEntity对象。这个类未声明public,所以只能包内访问。而且该类的contentType属性是private final类型。就算可以通过对象拿到这个属性,也无法修改。所以我只能通过反射来修改。

	private static void removeContentTypeChraset(String encoding, HttpEntity entity) {
		try {
			Class<?> clazz = entity.getClass();
			Field field = clazz.getDeclaredField("contentType");
			field.setAccessible(true); //将字段的访问权限设为true:即去除private修饰符的影响
			if(Modifier.isFinal(field.getModifiers())){
				Field modifiersField = Field.class.getDeclaredField("modifiers"); //去除final修饰符的影响,将字段设为可修改的  
				modifiersField.setAccessible(true);  
				modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL);  
			}
			BasicHeader o = (BasicHeader) field.get(entity);
			field.set(entity, new BasicHeader(HTTP.CONTENT_TYPE, o.getValue().replace("; charset="+encoding,"")));
		} catch (NoSuchFieldException e) {
			Utils.exception(e);
		} catch (SecurityException e) {
			Utils.exception(e);
		} catch (IllegalArgumentException e) {
			Utils.exception(e);
		} catch (IllegalAccessException e) {
			Utils.exception(e);
		}
	}

       剩下的就是HttpConfig的修改了,所有参数都是通过这个对象封装的。对于上传,我没想再建立一个file数组对象来传递参数,因为在上传时,可能还会有其他参数,都得需要通过map来传递,所以上传也直接借助map来完成。

/**
	 * 上传文件时用到
	 */
	public HttpConfig files(String[] filePaths) {
		return files(filePaths, "file");
	}
	/**
	 * 上传文件时用到
	 * @param filePaths		待上传文件所在路径
	 */
	public HttpConfig files(String[] filePaths, String inputName) {
		return files(filePaths, inputName, false);
	}
	/**
	 * 上传文件时用到
	 * @param filePaths			待上传文件所在路径
	 * @param inputName		即file input 标签的name值,默认为file
	 * @param forceRemoveContentTypeChraset
	 * @return
	 */
	public HttpConfig files(String[] filePaths, String inputName, boolean forceRemoveContentTypeChraset) {
		synchronized (getClass()) {
			if(this.map==null){
				this.map= new HashMap<String, Object>();
			}
		}
		map.put(Utils.ENTITY_MULTIPART, filePaths);
		map.put(Utils.ENTITY_MULTIPART+".name", inputName);
		map.put(Utils.ENTITY_MULTIPART+".rmCharset", forceRemoveContentTypeChraset);
		return this;
	}
       map方法也做了相应的修改:
	/**
	 * 传递参数
	 */
	public HttpConfig map(Map<String, Object> map) {
		synchronized (getClass()) {
			if(this.map==null || map==null){
				this.map = map;
			}else {
				this.map.putAll(map);;
			}
		}
		return this;
	}

       最后,来一个测试类,

import java.util.HashMap;
import java.util.Map;

import com.tgb.ccl.http.common.HttpConfig;
import com.tgb.ccl.http.common.HttpCookies;
import com.tgb.ccl.http.common.Utils;
import com.tgb.ccl.http.exception.HttpProcessException;
import com.tgb.ccl.http.httpclient.HttpClientUtil;

/** 
 * 上传功能测试
 * 
 * @author arron
 * @date 2016年11月2日 下午1:17:17 
 * @version 1.0 
 */
public class TestUpload {

	public static void main(String[] args) throws HttpProcessException {
		//登录后,为上传做准备
		HttpConfig config = prepareUpload();
		
		String url= "http://test.free.800m.net:8080/up.php?action=upsave";//上传地址
		String[] filePaths = {"D:\\中国.txt","D:\\111.txt","C:\\Users\\160049\\Desktop\\中国.png"};//待上传的文件路径
		
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("path", "./tomcat/vhost/test/ROOT/");//指定其他参数
		config.url(url) //设定上传地址
				 .encoding("GB2312") //设定编码,否则可能会引起中文乱码或导致上传失败
				 .files(filePaths,"myfile",true)//.files(filePaths),如果服务器端有验证input 的name值,则请传递第二个参数,如果上传失败,则尝试第三个参数设置为true
				 .map(map);//其他需要提交的参数
		
		Utils.debug();//开启打印日志,调用 Utils.debug(false);关闭打印日志
		String r = HttpClientUtil.upload(config);//上传
		System.out.println(r);
		
	}

	/**
	 * 登录,并上传文件
	 * 
	 * @return
	 * @throws HttpProcessException
	 */
	private static HttpConfig prepareUpload() throws HttpProcessException {
		String url ="http://test.free.800m.net:8080/";
		String loginUrl = url+"login.php";
		String indexUrl = url+"index.php";
		HttpCookies cookies = HttpCookies.custom();
		//启用cookie,用于登录后的操作
		HttpConfig config = HttpConfig.custom().context(cookies.getContext());
		Map<String, Object> map = new HashMap<String, Object>();
		map.put("user_name", "test");
		map.put("user_pass", "800m.net");
		map.put("action", "login");
		String loginResult = HttpClientUtil.post(config.url(loginUrl).map(map));
		
		System.out.println("是否登录成功:"+loginResult.contains("成功"));
		//打开主页
		HttpClientUtil.get(config.map(null).url(indexUrl));
		
		return config;
	}
	
}

       最新的完整代码请到GitHub上进行下载:https://github.com/Arronlong/httpclientUtil 。


       httpclientUtil (QQ交流群:548452686 httpclientUtil交流

版权声明:本文为博主原创文章,未经博主允许不得转载。如需转载请声明:【转自 http://blog.csdn.net/xiaoxian8023 】

HttpClient通过Post上传文件

在之前一段的项目中,使用Java模仿Http Post方式发送参数以及文件,单纯的传递参数或者文件可以使用URLConnection进行相应的处理。           但是项目中涉及到既要传递...
  • jdsjlzx
  • jdsjlzx
  • 2013年04月19日 19:28
  • 82603

Android网络编程之使用HttpClient批量上传文件(一)

我曾在《Android网络编程之使用HTTP访问网络资源》一文中介绍过HttpCient的使用,这里就不在累述了,感兴趣的朋友可以去看一下。在这里主要介绍如何通过HttpClient实现文件上传。 预...
  • fengyuzhengfan
  • fengyuzhengfan
  • 2014年10月09日 23:41
  • 14259

HttpClient使用MultipartEntityBuilder实现多文件上传

一、MultipartEntityBuilder 实现文件上传步骤    在HttpCient4.3之后上传文件主要使用的类是位于org.apache.http.entity.mime下的Mul...
  • zmx729618
  • zmx729618
  • 2017年12月04日 11:20
  • 654

Java客户端利用httpclient来同时上传文件和其他字符串参数

1.客户端代码如下:import java.io.File;import org.apache.http.HttpEntity; import org.apache.http.HttpResponse...
  • liufunan
  • liufunan
  • 2016年06月08日 14:57
  • 5777

HttpClients多文件上传连接 WebODM 中创建任务接口的方法

/** * 创建一个任务 * @param url WebODM 中的创建一个任务接口 url * @param path 文件所在本地文件夹的路径 * @param opt...
  • qq_38836118
  • qq_38836118
  • 2017年11月22日 10:33
  • 66

实现HTTP协议Get、Post和文件上传功能——设计和模块

本文主要讲解如何使用Python搭建Http服务器,并可以打印出Post和Get的请求参数。使用Http File Sever(HFS)搭建文件服务器,用于测试文件上传功能。还介绍了HTTP请求和数据...
  • breaksoftware
  • breaksoftware
  • 2015年05月28日 19:17
  • 14507

使用HttpClient4实现文件上传请求的发送,服务器端以MultipartFile形式接收(附依赖jar包地址)

今天学习使用了HttpClient4.2向服务端发送上传文件的请求,由于服务器端以MultipartFile形式接收,查询资料后决定使用HttpClient4.2实现,以下是实现代码(仅作测试使用):...
  • Coding13
  • Coding13
  • 2017年03月06日 18:02
  • 2152

Spring文件上传工具类,拿过去直接用就行了

package com.tangcy.npcmeeting.util; import com.tangcy.npcmeeting.utils.EncryptUtil; import org.spri...
  • u014598014
  • u014598014
  • 2017年05月10日 20:36
  • 642

文件上传工具类

/** * Copyright (c) 2005-2012 https://github.com/zhangkaitao * * Licensed under the Apache Licens...
  • johnjobs
  • johnjobs
  • 2014年10月30日 16:01
  • 1109

十个非常好用的文件上传工具(插件)

概述:下面列举了十个非常好用的文件上传工具,它们有些是针对jQuery的插件,有些是Ajax文件上传插件,还有支持多文件和大文件的批量上传工具。 根据维基百科的定义,文件上传是将本地的数...
  • baijianjun123456
  • baijianjun123456
  • 2016年09月05日 10:21
  • 3070
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:轻松把玩HttpClient之封装HttpClient工具类(九),添加多文件上传功能
举报原因:
原因补充:

(最多只允许输入30个字)