【HTTPClient 系列】HttpClient4.2.5上传文件,无中文文件名问题

转载请注明:http://blog.csdn.net/weijonathan/article/details/9328509

最近这段时间在研究HttpClient,想实现一个基于Http上传文件的功能。通过网上的很多文章,做了一个HttpClient上传文件的例子。

客户端:

public class HttpUploadFile {

	public static void main(String[] args) throws UnsupportedEncodingException {
		HttpClient client = new DefaultHttpClient();
		client.getParams().setParameter(
				CoreProtocolPNames.HTTP_CONTENT_CHARSET,
				Charset.forName("UTF-8"));
		HttpPost post = new HttpPost("http://localhost:8082/httpclient/upload");
		String name = new String("D:\\中文.txt".getBytes("UTF-8"), "UTF-8");
		File file = new File(name);
		MultipartEntity multipartEntity = new MultipartEntity();
		FileBody cbFileBody = new FileBody(file);
		multipartEntity.addPart("file", cbFileBody);

		post.setEntity(multipartEntity);
		HttpResponse response = null;
		String content = null;
		try {
			response = client.execute(post);
			content = EntityUtils.toString(response.getEntity());
		} catch (ClientProtocolException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (ParseException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		System.out.println(content);
		client.getConnectionManager().shutdown();
	}
}

服务器端:

@Controller
@RequestMapping("/httpclient")
public class HttpClientWeb {

	@RequestMapping(value = "/upload", method = RequestMethod.POST)
	public void uploadFile(HttpServletRequest request,
			HttpServletResponse response) throws IOException {
		String name = request.getParameter("name");
		DefaultMultipartHttpServletRequest req = (DefaultMultipartHttpServletRequest) request;
		request.setCharacterEncoding("UTF-8");
		Map<String, MultipartFile> files = req.getFileMap();
		for (String key : files.keySet()) {
			System.out.println(key);
			MultipartFile file = files.get(key);
			System.out.println(file.getName());
			System.out.println(file.getSize());
			System.out.println(file.getOriginalFilename());
			System.out.println(file.getBytes());

			FileOutputStream fileOutput;

			try {
				fileOutput = new FileOutputStream("d://hdytest//"
						+ file.getOriginalFilename());
				fileOutput.write(file.getBytes());
				fileOutput.flush();
				fileOutput.close();
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
}

然后开始测试了,测试Test.txt,a.map都通过了,而且MP3文件可以播放。很开心 偷笑

但是问题来了,当我上传中文.txt这个文件的时候,刷,一片红的。

DEBUG 07-15_09:29:40 DispatcherServlet.java 999 Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Dispatcher Servlet': assuming HandlerAdapter completed request handling 
DEBUG 07-15_09:29:40 CommonsFileUploadSupport.java 282 Cleaning up multipart file [file] with original filename [中文.txt], stored in memory 
DEBUG 07-15_09:29:40 FrameworkServlet.java 966 Successfully completed request 
DEBUG 07-15_10:36:14 DispatcherServlet.java 823 DispatcherServlet with name 'Spring MVC Dispatcher Servlet' processing POST request for [/httpclient/upload] 
DEBUG 07-15_10:36:14 CommonsFileUploadSupport.java 259 Found multipart file [file] of size 16 bytes with original filename [??.txt], stored in memory 
DEBUG 07-15_10:36:14 AbstractHandlerMethodMapping.java 226 Looking up handler method for path /httpclient/upload 
DEBUG 07-15_10:36:14 AbstractHandlerMethodMapping.java 233 Returning handler method [public void com.zqgame.m.controllers.HttpClientWeb.uploadFile(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException] 
java.io.FileNotFoundException: d:\hdytest\??.txt (文件名、目录名或卷标语法不正确。)
	at java.io.FileOutputStream.open(Native Method)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:179)
	at java.io.FileOutputStream.<init>(FileOutputStream.java:70)
	at com.zqgame.m.controllers.HttpClientWeb.uploadFile(HttpClientWeb.java:40)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)DEBUG 07-15_10:36:14 AbstractBeanFactory.java 246 Returning cached instance of singleton bean 'httpClientWeb' 
file
file
16
??.txt
[B@2b1682

	at org.springframework.web.method.support.InvocableHandlerMethod.invoke(InvocableHandlerMethod.java:219)
	at org.springframework.web.method.support.InvocableHandlerMethod.invokeForRequest(InvocableHandlerMethod.java:132)
	at org.springframework.web.servlet.mvc.method.annotation.ServletInvocableHandlerMethod.invokeAndHandle(ServletInvocableHandlerMethod.java:104)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.invokeHandleMethod(RequestMappingHandlerAdapter.java:745)
	at org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter.handleInternal(RequestMappingHandlerAdapter.java:686)
	at org.springframework.web.servlet.mvc.method.AbstractHandlerMethodAdapter.handle(AbstractHandlerMethodAdapter.java:80)
	at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:925)
	at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:856)
	at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:936)
	at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:838)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
	at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:812)
	at javax.servlet.http.HttpServlet.service(HttpServlet.java:820)
	at org.mortbay.jetty.servlet.ServletHolder.handle(ServletHolder.java:511)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1221)
	at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:88)
	at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
	at org.mortbay.jetty.servlet.ServletHandler$CachedChain.doFilter(ServletHandler.java:1212)
	at org.mortbay.jetty.servlet.ServletHandler.handle(ServletHandler.java:399)
	at org.mortbay.jetty.security.SecurityHandler.handle(SecurityHandler.java:216)
	at org.mortbay.jetty.servlet.SessionHandler.handle(SessionHandler.java:182)
	at org.mortbay.jetty.handler.ContextHandler.handle(ContextHandler.java:766)
	at org.mortbay.jetty.webapp.WebAppContext.handle(WebAppContext.java:450)
	at org.mortbay.jetty.handler.HandlerWrapper.handle(HandlerWrapper.java:152)
	at org.mortbay.jetty.Server.handle(Server.java:326)
	at org.mortbay.jetty.HttpConnection.handleRequest(HttpConnection.java:542)DEBUG 07-15_10:36:14 DispatcherServlet.java 999 Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Dispatcher Servlet': assuming HandlerAdapter completed request handling 
DEBUG 07-15_10:36:14 CommonsFileUploadSupport.java 282 Cleaning up multipart file [file] with original filename [??.txt], stored in memory 
DEBUG 07-15_10:36:14 FrameworkServlet.java 966 Successfully completed request 

	at org.mortbay.jetty.HttpConnection$RequestHandler.content(HttpConnection.java:945)
	at org.mortbay.jetty.HttpParser.parseNext(HttpParser.java:756)
	at org.mortbay.jetty.HttpParser.parseAvailable(HttpParser.java:218)
	at org.mortbay.jetty.HttpConnection.handle(HttpConnection.java:404)
	at org.mortbay.io.nio.SelectChannelEndPoint.run(SelectChannelEndPoint.java:410)
	at org.mortbay.thread.QueuedThreadPool$PoolThread.run(QueuedThreadPool.java:582)

这下蒙了,在网上找了好多资料,都说要修改源码,那好,修改源码开始

按照网上说的,修改EncodingUtils,把默认的"US-ASCII"修改为UTF-8,重新编译,测试,不通过。

修改为GBK,编译,测试,不通过;

修改为ISO-8859-1,编译,测试,不通过;

我的心都凉了,为什么别人有用的方法我都没用。发火


想了下,还是自己看源码吧!

就一步步跟源码下去,思路是这样的,因为file是存放在FileBody里面,而FileBody却是放在MultipartEntity里面,

我从这里开始下手的,结果查看到MultipartEntity的构造函数里面是这样的。这里有三个构造函数

public MultipartEntity(
            HttpMultipartMode mode,
            String boundary,
            Charset charset) {
        super();
        if (boundary == null) {
            boundary = generateBoundary();
        }
        if (mode == null) {
            mode = HttpMultipartMode.STRICT;
        }
        this.multipart = new HttpMultipart("form-data", charset, boundary, mode);
        this.contentType = new BasicHeader(
                HTTP.CONTENT_TYPE,
                generateContentType(boundary, charset));
        this.dirty = true;
    }

    /**
     * Creates an instance using the specified {@link HttpMultipartMode} mode.
     * Boundary and charset are set to {@code null}.
     * @param mode the desired mode
     */
    public MultipartEntity(final HttpMultipartMode mode) {
        this(mode, null, null);
    }

    /**
     * Creates an instance using mode {@link HttpMultipartMode#STRICT}
     */
    public MultipartEntity() {
        this(HttpMultipartMode.STRICT, null, null);
    }
看到第一个构造函数public MultipartEntity( HttpMultipartMode mode,String boundary,  Charset charset

Charset charset 感觉有点眉目了!接下来看到MultipartEntity把charset传给HttpMultipart,然后跟了进去

public HttpMultipart(final String subType, final Charset charset, final String boundary, HttpMultipartMode mode) {
        super();
        if (subType == null) {
            throw new IllegalArgumentException("Multipart subtype may not be null");
        }
        if (boundary == null) {
            throw new IllegalArgumentException("Multipart boundary may not be null");
        }
        this.subType = subType;
        this.charset = charset != null ? charset : <span style="color:#ff0000;"><strong>MIME.DEFAULT_CHARSET;</strong></span>
        this.boundary = boundary;
        this.parts = new ArrayList<FormBodyPart>();
        this.mode = mode;
    }
大家注意MIME.DEFAULT_CHARSET这里,我们来看下这个类

/**
 *
 * @since 4.0
 */
public final class MIME {

    public static final String CONTENT_TYPE          = "Content-Type";
    public static final String CONTENT_TRANSFER_ENC  = "Content-Transfer-Encoding";
    public static final String CONTENT_DISPOSITION   = "Content-Disposition";

    public static final String ENC_8BIT              = "8bit";
    public static final String ENC_BINARY            = "binary";

    /** The default character set to be used, i.e. "US-ASCII" */
    public static final Charset DEFAULT_CHARSET      = Charset.forName("US-ASCII");

}

默认为US-ASCII。原来是这样的。

我们看下我们之前的调用方法

MultipartEntity multipartEntity = new MultipartEntity();是这样调用的。

我们可以看到上面的MultipartEntity()构造方法调用

 public MultipartEntity() {
        this(HttpMultipartMode.STRICT, null, null);
    }
就是说默认的charset给的值就是null,而到里面程序就会给它赋一个默认值US-ASCII。

那么现在我们修改下代码。只需要修改一个地方

MultipartEntity multipartEntity = new MultipartEntity();
修改为

MultipartEntity multipartEntity = new MultipartEntity(
				HttpMultipartMode.BROWSER_COMPATIBLE, null,
				Charset.forName("UTF-8"));

将之前修改编译EncodingUtils的jar包去掉重新下载放入。测试

DEBUG 07-15_10:50:14 DispatcherServlet.java 823 DispatcherServlet with name 'Spring MVC Dispatcher Servlet' processing POST request for [/httpclient/upload] 
DEBUG 07-15_10:50:14 CommonsFileUploadSupport.java 259 Found multipart file [file] of size 16 bytes with original filename [中文.txt], stored in memory 
DEBUG 07-15_10:50:14 AbstractHandlerMethodMapping.java 226 Looking up handler method for path /httpclient/upload 
DEBUG 07-15_10:50:14 AbstractHandlerMethodMapping.java 233 Returning handler method [public void com.zqgame.m.controllers.HttpClientWeb.uploadFile(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse) throws java.io.IOException] 
DEBUG 07-15_10:50:14 AbstractBeanFactory.java 246 Returning cached instance of singleton bean 'httpClientWeb' 
file
file
16
<strong><span style="color:#ff0000;">中文.txt</span></strong>
[B@cd2192
DEBUG 07-15_10:50:14 DispatcherServlet.java 999 Null ModelAndView returned to DispatcherServlet with name 'Spring MVC Dispatcher Servlet': assuming HandlerAdapter completed request handling 
DEBUG 07-15_10:50:14 CommonsFileUploadSupport.java 282 Cleaning up multipart file [file] with original filename [中文.txt], stored in memory 
DEBUG 07-15_10:50:14 FrameworkServlet.java 966 Successfully completed request 


恭喜我吧!万事大吉,通过了! 大笑



评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值