转载请注明: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(); } } } }
但是问题来了,当我上传中文.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"); }
我们看下我们之前的调用方法
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"));
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