Java模拟表单文件上传(微信/中文名乱码问题)

2023-11-07 16:40

本文主要是介绍Java模拟表单文件上传(微信/中文名乱码问题),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

Http请求

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

------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关闭流

/*** 模拟上传文件(远程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("\r\n");contentBody.append("Content-Disposition: form-data;name=\"media\";filename=\""+ filename + "\"\r\n");contentBody.append("Content-Type:application/octet-stream\r\n\r\n");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("\r\n").append("--").append(BOUNDARY).append("\r\n");paramData.append("Content-Disposition: form-data;name=\""+key+"\";");paramData.append("\r\n");paramData.append("\r\n");paramData.append("\""+params.get(key)+"\"");System.out.println(paramData);out.write(paramData.toString().getBytes());}}// 最后一个片段结尾要用--表示byte[] end_data = ("\r\n--" + BOUNDARY + "--\r\n").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可以共享。

返回说明

{"type": "image","media_id": "1G6nrLmr5EC3MMb_-zK1dDdzmd0p7cNliYu9V5w7o8K0","created_at": "1380000000"
}

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

上传文件

    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,文件流的实现版本,总体还是解决了这个问题。

多文件上传版本

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("\r\n").append("--").append(BOUNDARY).append("\r\n");contentBody.append("Content-Disposition: form-data; name=\"").append(key + "\"");contentBody.append("\r\n").append("\r\n").append(params.get(key)).append("\r\n");}}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("\r\n").append("--").append(BOUNDARY).append("\r\n");contentBody.append("Content-Disposition:form-data; name=\"").append("media\"; ").append("filename=\"").append(file.getName() + "\"").append("\r\n" + "Content-Type:application/octet-stream").append("\r\n" + "Content-Transfer-Encoding: binary" + "\r\n" + "\r\n");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 = ("\r\n--" + BOUNDARY + "--\r\n").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 实现文件上传版本

    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
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模拟表单文件上传(微信/中文名乱码问题)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/364942

相关文章

Java实现检查多个时间段是否有重合

《Java实现检查多个时间段是否有重合》这篇文章主要为大家详细介绍了如何使用Java实现检查多个时间段是否有重合,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录流程概述步骤详解China编程步骤1:定义时间段类步骤2:添加时间段步骤3:检查时间段是否有重合步骤4:输出结果示例代码结语作

Java中String字符串使用避坑指南

《Java中String字符串使用避坑指南》Java中的String字符串是我们日常编程中用得最多的类之一,看似简单的String使用,却隐藏着不少“坑”,如果不注意,可能会导致性能问题、意外的错误容... 目录8个避坑点如下:1. 字符串的不可变性:每次修改都创建新对象2. 使用 == 比较字符串,陷阱满

Java判断多个时间段是否重合的方法小结

《Java判断多个时间段是否重合的方法小结》这篇文章主要为大家详细介绍了Java中判断多个时间段是否重合的方法,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录判断多个时间段是否有间隔判断时间段集合是否与某时间段重合判断多个时间段是否有间隔实体类内容public class D

IDEA编译报错“java: 常量字符串过长”的原因及解决方法

《IDEA编译报错“java:常量字符串过长”的原因及解决方法》今天在开发过程中,由于尝试将一个文件的Base64字符串设置为常量,结果导致IDEA编译的时候出现了如下报错java:常量字符串过长,... 目录一、问题描述二、问题原因2.1 理论角度2.2 源码角度三、解决方案解决方案①:StringBui

Java覆盖第三方jar包中的某一个类的实现方法

《Java覆盖第三方jar包中的某一个类的实现方法》在我们日常的开发中,经常需要使用第三方的jar包,有时候我们会发现第三方的jar包中的某一个类有问题,或者我们需要定制化修改其中的逻辑,那么应该如何... 目录一、需求描述二、示例描述三、操作步骤四、验证结果五、实现原理一、需求描述需求描述如下:需要在

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

JavaScript中的reduce方法执行过程、使用场景及进阶用法

《JavaScript中的reduce方法执行过程、使用场景及进阶用法》:本文主要介绍JavaScript中的reduce方法执行过程、使用场景及进阶用法的相关资料,reduce是JavaScri... 目录1. 什么是reduce2. reduce语法2.1 语法2.2 参数说明3. reduce执行过程

如何使用Java实现请求deepseek

《如何使用Java实现请求deepseek》这篇文章主要为大家详细介绍了如何使用Java实现请求deepseek功能,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录1.deepseek的api创建2.Java实现请求deepseek2.1 pom文件2.2 json转化文件2.2

Java调用DeepSeek API的最佳实践及详细代码示例

《Java调用DeepSeekAPI的最佳实践及详细代码示例》:本文主要介绍如何使用Java调用DeepSeekAPI,包括获取API密钥、添加HTTP客户端依赖、创建HTTP请求、处理响应、... 目录1. 获取API密钥2. 添加HTTP客户端依赖3. 创建HTTP请求4. 处理响应5. 错误处理6.

Spring AI集成DeepSeek的详细步骤

《SpringAI集成DeepSeek的详细步骤》DeepSeek作为一款卓越的国产AI模型,越来越多的公司考虑在自己的应用中集成,对于Java应用来说,我们可以借助SpringAI集成DeepSe... 目录DeepSeek 介绍Spring AI 是什么?1、环境准备2、构建项目2.1、pom依赖2.2