概述

兼容项目的jdk1.7,只能使用resteasy3.0.19.Final版本,之后的版本需要1.8支持

使用此版本进行post提交时,使用"multipart/form-data"进行表单提交,对于中文文件名会出现乱码,出现乱码的本质就是字符转码失败.

相对于web访问中对字符进行不同编码之间的转换应用场景,基本就是读取流中自己数据,字符而言,流中存储的是字节,服务器处理时按照你解码的方式进行编码获取正确的字符,然后服务器根据字符进行相应的处理.出现乱码的原因主要是服务器浏览器编码方式不一致,比如浏览器用uft-8进行编码,而服务器用us_asii就会出现乱码(本次乱码原因就是在此)

demo

开始测试时以为是前端解析参数时出现的问题,后来才发现问题其实出现现在resteasy自己的参数解析

jsp测试

<%@ page language="java" contentType="text/html; charset=UTF-8"pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<form method="post" action="xxx" enctype="multipart/form-data">选择一个文件:<input type="file" name="uploadFile" /><br/><br/><input type="submit" value="上传" />
</form>
</body>
</html>

起初以为是前端页面解析的问题,在form 表单里添加accept-charset=''utf-8''也没用, 其实accept-charset默认值是 "unknown",表示表单的字符集与包含表单的文档的字符集相同.

使用jetty8 默认 处理编码utf-8

查看post的请求体payload

------WebKitFormBoundaryRAPt6XbF1drWs6Kx
Content-Disposition: form-data; name="uploadFile"; filename="我是中文.xlsx"
Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet------WebKitFormBoundaryRAPt6XbF1drWs6Kx--

这个请求体,然后在contentType中加入charset=utf-8,其实这个参数怎么起作用,作用于什么内容,是跟框架的解析有关系,一般的框架处理解析的是其内容

查看MultipartFormDataInput是如何解析的

数据以字节流的形式InputStream传输到后台,经过web一系列的参数解析(有时间再搞出来),最终需要绑定到MultipartFormDataInput的实现类MultipartFormDataInputImpl,绑定过程有一个解析过程(预处理->设置载体(Message)->设置解析器MimeStreamParser->设置内容处理器ContentHandler),比如MultipartFormDataInputImpl的父类设置载体(载体中设置解析器,解析器中设置内容处理):

   public void parse(InputStream is) throws IOException{mimeMessage = new BinaryMessage(addHeaderToHeadlessStream(is));extractParts();}

到达这里时,InputStream 中文件名存储的是utf-8的自己数组

最后回调到MimeStreamParser

    public void parse(InputStream is) throws MimeException, IOException {mimeTokenStream.parse(is);OUTER: for (;;) {int state = mimeTokenStream.getState();switch (state) {case MimeTokenStream.T_BODY:BodyDescriptor desc = mimeTokenStream.getBodyDescriptor();InputStream bodyContent;if (contentDecoding) {bodyContent = mimeTokenStream.getDecodedInputStream(); } else {bodyContent = mimeTokenStream.getInputStream(); }handler.body(desc, bodyContent);break;case MimeTokenStream.T_END_BODYPART:handler.endBodyPart();break;case MimeTokenStream.T_END_HEADER:handler.endHeader();break;case MimeTokenStream.T_END_MESSAGE:handler.endMessage();break;case MimeTokenStream.T_END_MULTIPART:handler.endMultipart();break;case MimeTokenStream.T_END_OF_STREAM:break OUTER;case MimeTokenStream.T_EPILOGUE:handler.epilogue(mimeTokenStream.getInputStream());break;case MimeTokenStream.T_FIELD:handler.field(mimeTokenStream.getField());break;case MimeTokenStream.T_PREAMBLE:handler.preamble(mimeTokenStream.getInputStream());break;case MimeTokenStream.T_RAW_ENTITY:handler.raw(mimeTokenStream.getInputStream());break;case MimeTokenStream.T_START_BODYPART:handler.startBodyPart();break;case MimeTokenStream.T_START_HEADER:handler.startHeader();break;case MimeTokenStream.T_START_MESSAGE:handler.startMessage();break;case MimeTokenStream.T_START_MULTIPART:handler.startMultipart(mimeTokenStream.getBodyDescriptor());break;default:throw new IllegalStateException("Invalid state: " + state);}state = mimeTokenStream.next();}}

这里使用到java的新进后出的Stack与反射,对post payload的这种field进行解析,这里主要讲对header的处理

一个完整的header完整的处理流程

  • startHeader()->field()->endHeader();
  • startHeader()-往stack中push一个new Header())
  • endHeader()-stack peek一个出来(新进后出的原则,也就是把你刚刚放进去的header拿出来)

重点在field()中

    public void field(Field field) throws MimeException {expect(Header.class);Field parsedField = AbstractField.parse(field.getRaw()); ((Header) stack.peek()).addField(parsedField);}
    public static ParsedField parse(final ByteSequence raw) throws MimeException {String rawStr = ContentUtil.decode(raw);return parse(raw, rawStr);}
    public static String decode(ByteSequence byteSequence) {return decode(CharsetUtil.US_ASCII, byteSequence, 0, byteSequence.length());}

看到以上就明白了,最后以US_ASCII编码,而且是写死的,没什么可配置修改,至此乱码原因找到了.因为基本没什么配置,所以一些解析器都是使用的默认,要修复这个bug大体上有三个个思路

  • 第一种:filename与文件分开.分开有两种方法,第一种客户端另外传递一个filename参数(不推荐),第二种服务器单独把filename取出来,独自解析
  • 第二种:使用指定或者自定义的解析器
  • 第三种:改源码,最简单,最暴力第一种尝试失败了

我要获取content-disposition,必须要读inputstream,但是我用jetty使用的是httpinput,这个inputstream不允许重复读(不支持reset,大概是保护数据只被读取一次)

第二种,自定义一个MultipartFormDataReader,自己对内容进行处理,这么做相当于对自定义了一个MultipartFormDataInput,复杂

第三种,改源码

resteasy 还有一种@MultipartForm,这种底层原理就是MultipartFormDataInput(二者默认的formdatareader不一样,一个是MultipartFormDataReader,一个是MultipartFormAnnotationReader),最后将内容解析单独拿出来封装到你制定的Entity中,但是这种需要你额外在entity指定file与filename的key(@FormParam("selectedFile"),也就意味着客户端必须与服务器的key值一样,太挫)

或者直接使用原生的requeset的common uploadfile

以下代码仅供测试用例:

        ServletFileUpload upload = new ServletFileUpload();FileItemIterator fileIterator = upload.getItemIterator(request);String parent="C:\\Users\\admin\\Desktop\\temp\\";while (fileIterator.hasNext()) {FileItemStream item = fileIterator.next();String filename=item.getName();File file = new  File(parent,filename);FileUtils.writeByteArrayToFile(file, IOUtils.toByteArray(item.openStream()));}return "Done";

附录编码笔记

任何编码,都有特定的机器语言与之对应,最终是以字节的形式存储,不同的编码其存储的字节不同.简单的讲,一个符号对应编码表中一个数字,不同的编码格式决定了这个数字以什么样的方式存储.以"中"字为例


       "中"字在utf-8与unicode中各自对应的数字(可以理解为唯一标识)不一样

再来看看字节

utf-8存储是三个字节,而unicode存储的是四个字节.存储多少个自己根据的就是编码规则来定.而unicode与utf等其他的区别,主要是前者是code point,后者是UCS Transformation Format

获取虚拟机编码Charset.defaultCharset(),也就是工作运行的编码(比如存储class文件的编码(可以通过文件的properties属性查看)),但是在java内存中,String是以unicode编码的形式存在的(这个就真不知道怎么去测试了),我们用String.getBytes(charset),其底层就是讲内存中unicode编码的字符串转为指定编码的字节(因为unicode基本可以向任何一种编码格式转换).

resteasy 上传表单文件名乱码相关推荐

  1. 文件上传表单 上传文件的细节 文件上传下载和数据库结合

    1 文件上传表单    1)上传文件的本质是文本复制的过程    2)技术层面,在Java中一定会用到IO操作,主要以二进制方式读写    3)传统方式下,对于上传文件字段不同的浏览器有着不同的解析方 ...

  2. 利用mysql实现上传和下载_文件上传表单 上传文件的细节 文件上传下载和数据库结合...

    1 文件上传表单 1)上传文件的本质是文本复制的过程 2)技术层面,在Java中一定会用到IO操作,主要以二进制方式读写 3)传统方式下,对于上传文件字段不同的浏览器有着不同的解析方式,例如: IE6 ...

  3. 【Flask】 结合wtforms的文件上传表单

    表单中的文件上传 基本的表单渲染,表单类设置等等就不多说了,参看另一个文章即可.但是那篇文章里没有提到对于FileField,也就是上传文件的表单字段是如何处理,后端又是如何实现接受上传过来的文件的. ...

  4. php 上传文件名乱码,php上传文件时文件名乱码怎么办

    php上传文件时文件名乱码的解决方法:首先在脚本头部添加[header("Content-type: text/html; charset=utf-8");]:然后利用iconv( ...

  5. html js文件域val,js实现文件上传表单域美化特效

    一款效果非常时尚的文件上传表单域美化特效,下面给出制作的简要教程. 先上几个效果饱饱眼福: 使用方法 这些文件上传域的美化使用的方法都是隐藏原生的元素,然后使用一个元素来制作美化效果. @H_502_ ...

  6. Android之PC浏览器上传表单格式大文件到手机客户端read函数阻塞问题

    1 .问题 PC浏览器上传表单格式大文件到手机服务器端,然后read文件真实数据时候出现阻塞. 比如 User-Agent: PostmanRuntime/7.26.1Accept: */*Cache ...

  7. android 阻塞式函数,Android之PC浏览器上传表单格式大文件到手机客户端read函数阻塞问题...

    1 .问题 PC浏览器上传表单格式大文件到手机服务器端,然后read文件真实数据时候出现阻塞. 比如 User-Agent: PostmanRuntime/7.26.1 Accept: */* Cac ...

  8. php 美化js文件,js实现文件上传表单域美化特效_javascript技巧

    一款效果非常时尚的文件上传表单域美化特效,下面给出制作的简要教程. 先上几个效果饱饱眼福: 使用方法 这些文件上传域的美化使用的方法都是隐藏原生的元素,然后使用一个元素来制作美化效果. HTML结构 ...

  9. dwz 表单提交 html,DWZ文件上传表单提交

    文件上传表单提交 因为Ajax不支持enctype="multipart/form-data" 所以用隐藏iframe来处理无刷新表单提交. 或 服务器端响应 DWZ-v1.2版本 ...

最新文章

  1. 开发加速使用maven国内源,感谢阿里技术团队,良心团队!
  2. 【深度学习】卷积越大性能越强!RepLKNet一作在线分享:Transformer阴影下的超大卷积核复兴...
  3. JAVA8 Optional新特性和使用详解
  4. mvc if 显示html,asp.net mvc - 在MVC3 Razor中,如何在动作中获取渲染视图的html?
  5. hive 判断子字符串_Java中检查空字符串(null或空白)的方法有几种?
  6. [css] 请使用css写一个多级的下拉菜单
  7. 写一个简单控制台启动的mcv服务
  8. 华为nova9系列正式官宣:9月23日震撼上市
  9. IAR下STM32进入HardFault_Handler
  10. kvm虚拟机块对齐问题研究
  11. Less(v3.9.0)使用详解—变量
  12. mysql 纵列转横列_mysql行列转换方法总结
  13. 一个开源vue网站博客,nuxt开源网站,前后端分离项目
  14. oracle erase,Arc SDE forOracle实现erase空间分析计算
  15. matlab实现聚类分析的优点,MATLAB感悟(3)--聚类分析
  16. linux恢复树莓派内存卡容量,找回树莓派SD卡剩余空间
  17. python对行为进行推理_一道有意思推理题,用python来解答
  18. 洛谷P3369 AVL树
  19. 海洋cms采集后无法播放解决方法
  20. 博弈论中的MinMax搜索算法

热门文章

  1. matlab实现移相,MATLAB中的SISOTOOL在数字式移相全桥中的
  2. 前端代码优化(持续更新中)
  3. 全新的文件启动方式 HapiGo 1.0.2(15) 中文版
  4. java coalesce_java-具有休眠条件的COALESCE函数
  5. 台式计算机显示屏不亮,台式机屏幕不亮怎么办
  6. Jq中prop()和attr()之间的区别
  7. hph smarty下载地址
  8. 老K推荐3部好片,豆瓣9分神作,刷爆B站!
  9. 东东学打牌 Week9作业B题
  10. 安装zabbix及修改yum源为163的CentOS源