我是靠谱客的博主 甜甜电灯胆,这篇文章主要介绍Java模拟表单文件上传(微信/中文名乱码问题),现在分享给大家,希望可以做个参考。

最近在做微信企业号,在上传素材文件时遇到了点问题,主要是中文文件名乱码的问题,开始使用的是httpclient3.x的api去实现,文件上传没问题,就是名称是中文的时候死活都是乱码,自己也设置了很多地方的编码为utf-8,也没有用,后来百度,谷歌,也没有找到解决方案,或许解决方案是有效的,但是到了微信这边就没有用了,后来干脆使用HttpURLConnection解决了问题,另外经过半天鼓捣,也解决了一些其他相关性的问题,对文件上传也有了更深的了解。不过后面用httpclient4.x试了一下,发现也是可以解决问题的。

Http请求

自己写了一个表单,上传文件,提交表单,查看http请求,可以看到。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
------WebKitFormBoundary7blBEBTiZtWOX8LI Content-Disposition: form-data; name="id" ------WebKitFormBoundary7blBEBTiZtWOX8LI Content-Disposition: form-data; name="msgtype" file ------WebKitFormBoundary7blBEBTiZtWOX8LI Content-Disposition: form-data; name="uploadImage"; filename="0.png" Content-Type: image/png ------WebKitFormBoundary7blBEBTiZtWOX8LI--

表单设置成multipart/form-data之后表单内容就变成上面的格式了,如上表单中有两个属性id,msgtype,其中id内容为空,msgtype值为file,然后还有一个文件,
Content-Disposition: form-data; name=”uploadImage”; filename=”0.png”
其中name是input file 的name值,filename是文件名,注意模拟表单提交的时候,filename必须要有值,而且是要带后缀的,否则提交不成功。

HttpURLConnection实现表单文件提交

这里使用的IOUtils是commons.io的包,也是直接使用close关闭流

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
/** * 模拟上传文件(远程URL) * * @Title uploadFile * @param uploadUrl 上传路径 * @param picUrl 远程图片URL * @param params 参数内容 * @return * @throws IOException * @author zh * @date 2016年6月30日 下午4:22:10 */ public static String uploadFile(String uploadUrl, URL picUrl, Map<String,String> params) throws IOException { return uploadFile(uploadUrl, picUrl.openStream(), picUrl.getFile().substring(picUrl.getFile().lastIndexOf("/")) + 1, params); } /** * 模拟上传文件(本地文件) * * @Title uploadFile * @param uploadUrl 上传路径 * @param picUrl 本地文件 * @param params 参数内容 * @return * @throws IOException * @author zh * @date 2016年6月30日 下午4:23:15 */ public static String uploadFile(String uploadUrl, File file, Map<String,String> params) throws IOException { return uploadFile(uploadUrl, new FileInputStream(file), file.getName(), params); } /** * 模拟上传文件(输入流) * * @Title uploadFile * @param uploadUrl 上传链接 * @param is 输入流 * @param filename 文件名 * @param params 参数 * @return * @throws IOException * @author zh * @date 2016年6月30日 下午4:08:52 */ public static String uploadFile(String uploadUrl, InputStream is, String filename, Map<String,String> params) throws IOException { URL urlGet = new URL(uploadUrl); HttpURLConnection conn = (HttpURLConnection) urlGet.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("connection", "Keep-Alive"); conn.setRequestProperty("user-agent", DEFAULT_USER_AGENT); conn.setRequestProperty("Charsert", "UTF-8"); // 定义数据分隔线 String BOUNDARY = "----WebKitFormBoundary7blBEBTiZtWOX8LI"; conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); OutputStream out = new DataOutputStream(conn.getOutputStream()); // 文件内容 StringBuilder contentBody = new StringBuilder(); contentBody.append("--").append(BOUNDARY).append("rn"); contentBody.append("Content-Disposition: form-data;name="media";filename=""+ filename + ""rn"); contentBody.append("Content-Type:application/octet-streamrnrn"); System.out.println(contentBody); out.write(contentBody.toString().getBytes()); DataInputStream fs = new DataInputStream(is); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = fs.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } IOUtils.closeQuietly(fs); // 参数内容 if (params != null && params.size() > 0) { for (String key : params.keySet()) { StringBuilder paramData = new StringBuilder(); paramData.append("rn").append("--").append(BOUNDARY).append("rn"); paramData.append("Content-Disposition: form-data;name=""+key+"";"); paramData.append("rn"); paramData.append("rn"); paramData.append("""+params.get(key)+"""); System.out.println(paramData); out.write(paramData.toString().getBytes()); } } // 最后一个片段结尾要用--表示 byte[] end_data = ("rn--" + BOUNDARY + "--rn").getBytes(); out.write(end_data); out.flush(); IOUtils.closeQuietly(out); // 定义BufferedReader输入流来读取URL的响应 InputStream in = conn.getInputStream(); BufferedReader read = new BufferedReader(new InputStreamReader(in, Charsets.UTF_8)); String valueString = null; StringBuffer bufferRes = null; bufferRes = new StringBuffer(); while ((valueString = read.readLine()) != null){ bufferRes.append(valueString); } IOUtils.closeQuietly(in); // 关闭连接 if (conn != null) { conn.disconnect(); } return bufferRes.toString(); }

微信企业号上传临时素材

  • 请求说明
    Https请求方式: POST

https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token=ACCESS_TOKEN&type=TYPE

  • 参数说明
    参数 必须 说明
    这里写图片描述
    权限说明
    完全公开。所有管理组均可调用,media_id可以共享。

返回说明

复制代码
1
2
3
4
5
{ "type": "image", "media_id": "1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0", "created_at": "1380000000" }

说实话,微信的文档有些地方做的真的不怎么样,而且企业号现在功能也不是太全,比如SaaS套件第三方应用有授权安装的API,却没有取消授权安装的API,有些东西值要调几个API才可以得到,可能他们有他们的考虑吧,不说了,继续说遇到的问题。
使用上面这种方法调用上传素材,然后得到media_id去发送消息,发送文件的时候,如果是中文名的文件,也不会出现乱码了,当然这个上传文件不仅是在微信的时候用,平时需要的各种环境都可以使用,这里拿微信只是举例,如果要使用httpclient,那还是推荐使用httpclient4.x以上的版本吧,貌似这个解决方案多一点。

上传文件

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
public static void main(String[] args) throws Exception { File file = new File("E:\中文图片.jpg"); String access_token = "AasbMO9SCcF3HO0jVLnddQuZo4pUmSYHieqC56vQbBvQgqGWW8spQG5CtWT6Vcjh"; String uploadUrl = "https://qyapi.weixin.qq.com/cgi-bin/media/upload?type=file&access_token="+ CommonUtil.encode(access_token); // 本地文件 String upload_response = uploadFile(uploadUrl, file, null); System.out.println(upload_response); // 远程URL文件提交上传 URL url = new URL("http://www.baidu.com/xx/xxx.zip"); String upload_response2 = uploadFile(uploadUrl, url, null); System.out.println(upload_response2); }

小结

这里走了很多弯路,因为在前台页面不可能带上Access_token,为了安全,只能先将文件上传到服务器,然后发送请求到微信,这里就出现了一个问题,服务器是linux的,中文名的文件会乱码,起初是统一替换文件名为一串时间戳的字符,但是在发送消息,类型为文件时,会显示文件名,这样不好,所以必须要显示原文件名,原以为必须要再本地存一份,然后提交给微信,后来发现,远程URL,文件流都可以提交,所以问题就好解决了,本地不需要存了,然后得到文件流,然后得到原文件名,推送给微信,上面给出的三个版本,本地文件,远程URL,文件流的实现版本,总体还是解决了这个问题。

多文件上传版本

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
private static final String DEFAULT_USER_AGENT = "Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/33.0.1750.146 Safari/537.36"; /** * 模拟上传文件请求 * * @Title uploadFile * @param uploadUrl 上传url * @param files 文件列表 * @param params 参数列表 * @return * @throws Exception * @author zh * @date 2016年6月30日 下午3:54:26 */ public String uploadFile(String uploadUrl, List<File> files, Map<String,String> params) throws Exception { String BOUNDARY = "----WebKitFormBoundary7blBEBTiZtWOX8LI"; String response = ""; HttpURLConnection conn = null; try { URL url = new URL(uploadUrl); conn = (HttpURLConnection) url.openConnection(); conn.setDoOutput(true); conn.setDoInput(true); conn.setUseCaches(false); conn.setRequestMethod("POST"); conn.setRequestProperty("Connection", "Keep-Alive"); conn.setRequestProperty("Charset", "UTF-8"); conn.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + BOUNDARY); StringBuffer contentBody = new StringBuffer(); OutputStream out = new DataOutputStream(conn.getOutputStream()); if (params != null && params.size() > 0) { for (String key : params.keySet()) { contentBody.append("rn").append("--").append(BOUNDARY).append("rn"); contentBody.append("Content-Disposition: form-data; name="").append(key + """); contentBody.append("rn").append("rn").append(params.get(key)).append("rn"); } } System.out.println(contentBody); out.write(contentBody.toString().getBytes("utf-8")); if(files != null && files.size() > 0){ for (File file : files) { contentBody = new StringBuffer(); contentBody.append("rn").append("--").append(BOUNDARY).append("rn"); contentBody.append("Content-Disposition:form-data; name="") .append("media"; ").append("filename="").append(file.getName() + """) .append("rn" + "Content-Type:application/octet-stream") .append("rn" + "Content-Transfer-Encoding: binary" + "rn" + "rn"); System.out.println(contentBody.toString()); out.write(contentBody.toString().getBytes("utf-8")); DataInputStream dis = new DataInputStream(new FileInputStream(file)); int bytes = 0; byte[] bufferOut = new byte[1024]; while ((bytes = dis.read(bufferOut)) != -1) { out.write(bufferOut, 0, bytes); } dis.close(); } } byte[] endData = ("rn--" + BOUNDARY + "--rn").getBytes(); out.write(endData); out.flush(); out.close(); // 读取返回数据 StringBuffer strBuf = new StringBuffer(); BufferedReader br = new BufferedReader(new InputStreamReader(conn.getInputStream())); String line = null; while ((line = br.readLine()) != null) { strBuf.append(line).append("n"); } response = strBuf.toString(); br.close(); } catch (Exception e) { System.out.println("发送POST请求出错。" + uploadUrl); e.printStackTrace(); } finally { if (conn != null) { conn.disconnect(); conn = null; } } return response; }

HttpClient3 实现文件上传版本

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public String uploadFile(String uploadUrl, List<File> files, Map<String,String> params) throws Exception { HttpClient client = new HttpClient(); PostMethod httpPost = new PostMethod(uploadUrl); httpPost.setRequestHeader("User-Agent", "AdUU"); httpPost.setRequestHeader("Accept-Language", "zh-cn,zh;q=0.5"); httpPost.setRequestHeader("Accept-Charset", "GBK,utf-8;q=0.7,;q=0.7"); httpPost.setRequestHeader("Connection", "keep-alive"); ArrayList<PartBase> parts = new ArrayList<PartBase>(); if (params != null && params.size() > 0) { for (String key : params.keySet()) { parts.add(new StringPart(key, params.get(key))); } } if (files != null && files.size() > 0) for (File file : files) { parts.add(new FilePart("upload",file.getName(), file)); } httpPost.setRequestEntity(new MultipartRequestEntity(parts.toArray(new Part[]{}), httpPost.getParams())); client.executeMethod(httpPost); StringBuilder response = new StringBuilder(); if (httpPost.getStatusCode() == 200) { BufferedReader reader = new BufferedReader( new InputStreamReader(httpPost.getResponseBodyAsStream(), "UTF-8")); String line; while ((line = reader.readLine()) != null) { response.append(line); } reader.close(); } return response.toString(); }

最后说一点,如果是实现微信上传的话, 最好还是使用HttpURLConnection版本,或者用httpclient4.x以上版本,这样就解决了中文文件名上传的问题。

HttpClient4.x 解决微信上传素材中文名乱码问题

测试通过版本:

  • httpclient 4.5.2
  • httpcore 4.4.4
  • httpmime 4.5.2
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import java.io.File; import java.io.IOException; import java.util.ArrayList; import java.util.List; import java.util.Map; import org.apache.http.Consts; import org.apache.http.HttpEntity; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.ContentType; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntityBuilder; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.util.EntityUtils; public class Upload { public static String uploadFile(String uploadUrl, List<File> files, Map<String,String> params) { CloseableHttpClient httpclient = HttpClients.createDefault(); CloseableHttpResponse response = null; HttpEntity entity = null; String result = ""; try{ HttpPost httpPost = new HttpPost(uploadUrl); httpPost.setHeader("User-Agent", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2723.3 Safari/537.36"); httpPost.setHeader("Accept-Language", "zh-cn,zh;q=0.5"); httpPost.setHeader("Accept-Charset", "UTF-8,utf-8;q=0.7,;q=0.7"); httpPost.setHeader("Connection", "keep-alive"); MultipartEntityBuilder multipartEntityBuilder = MultipartEntityBuilder.create(); // 这里是解决中文名乱码的关键,很多地方说设置HttpMultipartMode.BROWSER_COMPATIBLE,到微信这里就不行了 multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532); // 这里是个坑,乱码问题首先想到的是设置编码,可是设置这个之后,微信就报41005错误了。大坑大坑。。具体原因我也不清楚 // multipartEntityBuilder.setCharset(Consts.UTF_8); if(params != null && params.size() > 0){ for (Map.Entry<String, String> entry : params.entrySet()) { multipartEntityBuilder.addTextBody(entry.getKey(), entry.getValue().toString(), ContentType.create("text/plain", Consts.UTF_8)); } } if (files != null && files.size() > 0){ for (File file : files) { multipartEntityBuilder.addBinaryBody("media", file); } } httpPost.setEntity(multipartEntityBuilder.build()); response = httpclient.execute(httpPost); entity = response.getEntity(); if (response.getStatusLine().getStatusCode() == 200) { result = EntityUtils.toString(response.getEntity()); } }catch(Exception e){ e.printStackTrace(); }finally{ try { if(entity != null){ EntityUtils.consume(entity); } if(response != null){ response.close(); } if(httpclient != null){ httpclient.close(); } } catch (IOException e) { e.printStackTrace(); } } return result; } public static void main(String[] args) { String access_token = "1zavF25bdA-bunJWhbfM0TNdJyrfonKpCLwBk--TllY232BFXVcCapnNNTAigasb"; List<File> files = new ArrayList<File>(); files.add(new File("E:\中文.txt")); String result = uploadFile("https://qyapi.weixin.qq.com/cgi-bin/media/upload?access_token="+access_token+"&type=file", files, null); //{"type":"file","media_id":"1cn4gBj-jg6phmUZSqqRjbOYye56CdgPaTmgunfJ7yvhW44yaD-Py0mbA9lyZ6Ckhjli9JKIKibFv8kq385eWhg","created_at":1468382218} System.out.println(result); } }

注释部分是重点,网上很多解决方案都试过了,不行,首先想到的是设置编码,结果反而导致上传文件不成功了,微信返回41005之类的错误代码,很多地方说设置HttpMultipartMode.BROWSER_COMPATIBLE,到微信这里就不行了,所以解决中文名乱码的关键代码是multipartEntityBuilder.setMode(HttpMultipartMode.RFC6532);这样上传素材之后拿到的media_id然后去发送消息的时候,中文名就不会乱码了。

最后

以上就是甜甜电灯胆最近收集整理的关于Java模拟表单文件上传(微信/中文名乱码问题)的全部内容,更多相关Java模拟表单文件上传(微信/中文名乱码问题)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部