解决resteasy上传表单文件名乱码

摘要:
Dubbo在2.6版本后合并了dubbox的resteasy代码后,可以支持rest风格的接口发布,但是在使用form表单上传文件的时候,获取的文件名称是乱码。首先是一个resteasy上传的使用代码@POST@Path@Consumes@OverridepublicObjectuploadfile{System.out.println;//MultipartFormDataReaderMapmap=null;Map˂String,List˃uploadForm=input.getFormDataMap();//取得文件表单名ListinputParts=uploadForm.get;finalStringDIRCTORY="D:/temp/datainput/";initDirectory;InputStreaminputStream=null;OutputStreamoutStream=null;for{try{//文件名称StringfileName=getFileName;inputStream=inputPart.getBody;//把文件流保存;Filefile=newFile;byte[]buffer=newbyte[inputStream.available()];inputStream.read;outStream=newFileOutputStream;outStream.write;}catch{e.printStackTrace();}finally{if(null!=outStream){try{outStream.close();}catch{}}}}returnResponse.ok().build();}ViewCoderesteasy文件上传使用的Consumes使用的mediattype类型是MULTIPART_FORM_DATE这个mediatype使用的Provider使用的是org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader,其readForm的入口为publicMultipartFormDataInputreadFromthrowsIOException,WebApplicationException{Stringboundary=mediaType.getParameters().get;ifthrownewIOException;MultipartFormDataInputImplinput=newMultipartFormDataInputImpl;input.parse;returninput;}ViewCode在跟入上面代码的parse方法input.parse中的newBinaryMessage()构造函数中,MultipartInputImpl对http的head进行了解析privatestaticclassBinaryMessageextendsMessage{privateBinaryMessagethrowsIOException,MimeIOException{try{MimeStreamParserparser=newMimeStreamParser;StorageProviderstorageProvider;if(System.getProperty(DefaultStorageProvider.DEFAULT_STORAGE_PROVIDER_PROPERTY)!

Dubbo在2.6版本后合并了dubbox的resteasy代码后,可以支持rest风格的接口发布,但是在使用form表单上传文件的时候,获取的文件名称是乱码。

下面通过对源码分析一下原因,并提供一种可行的解决方法。

首先是一个resteasy上传的使用代码

解决resteasy上传表单文件名乱码第1张解决resteasy上传表单文件名乱码第2张
@POST
    @Path("/upload")
    @Consumes(MediaType.MULTIPART_FORM_DATA)
    @Override
    publicObject uploadfile(MultipartFormDataInput input, @Context HttpServletRequest request, @Context HttpServletResponse response) {
        System.out.println("进入业务逻辑");
//MultipartFormDataReader
        Map<String, Object> map = null;

        Map<String, List<InputPart>> uploadForm =input.getFormDataMap();
        //取得文件表单名
        List<InputPart> inputParts = uploadForm.get("file");

        final String DIRCTORY = "D:/temp/datainput/";
        initDirectory(DIRCTORY);
        InputStream inputStream = null;
        OutputStream outStream = null;
        for(InputPart inputPart : inputParts) {
            try{
                //文件名称  
                String fileName =getFileName(inputPart.getHeaders());
                inputStream = inputPart.getBody(InputStream.class, null);
                //把文件流保存;
                File file = new File(DIRCTORY +fileName);
                byte[] buffer = new byte[inputStream.available()];
                inputStream.read(buffer);
                outStream = newFileOutputStream(file);
                outStream.write(buffer); 
            } catch(IOException e) {
                e.printStackTrace();
            }finally{
                if(null !=inputStream){
                    try{
                        inputStream.close();
                    } catch(IOException e) {
                    }
                }
                if(null !=outStream){
                    try{
                        outStream.close();
                    } catch(IOException e) {
                    }
                }
            }

        }

        returnResponse.ok().build();
    }
View Code

resteasy文件上传使用的Consumes使用的mediattype类型是MULTIPART_FORM_DATE【@Consumes(MediaType.MULTIPART_FORM_DATA)】

这个mediatype使用的Provider使用的是org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader,其readForm的入口为

解决resteasy上传表单文件名乱码第3张解决resteasy上传表单文件名乱码第4张
   public MultipartFormDataInput readFrom(Class<MultipartFormDataInput> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throwsIOException, WebApplicationException
   {
     
      String boundary = mediaType.getParameters().get("boundary");
      if (boundary == null) throw newIOException(Messages.MESSAGES.unableToGetBoundary());
      MultipartFormDataInputImpl input = newMultipartFormDataInputImpl(mediaType, workers);
      input.parse(entityStream);
      returninput;
   }
View Code

在跟入上面代码的parse方法input.parse(entityStream)中的new BinaryMessage()构造函数中,MultipartInputImpl对http的head进行了解析

解决resteasy上传表单文件名乱码第5张解决resteasy上传表单文件名乱码第6张
private static class BinaryMessage extendsMessage
   {
      private BinaryMessage(InputStream is) throwsIOException, MimeIOException
      {
         try{
            MimeStreamParser parser = new MimeStreamParser(null);
            
            StorageProvider storageProvider;
            if (System.getProperty(DefaultStorageProvider.DEFAULT_STORAGE_PROVIDER_PROPERTY) != null) {
               storageProvider =DefaultStorageProvider.getInstance();
            } else{
               StorageProvider backend = newCustomTempFileStorageProvider();
               storageProvider = new ThresholdStorageProvider(backend, 1024);
            }
            parser.setContentHandler(new BinaryOnlyMessageBuilder(this, storageProvider));
            parser.parse(is); //此处未解析代码,未传入指定的字符串编码方式
         } catch(MimeException e) {
            throw newMimeIOException(e);
         }

      }
   }
View Code

在行parser.parse(is);中,采用的是apache-mime4j-1.6版本的流解析器,由于MultipartInputImpl在调用apache-mime4j的解析方法,没有可指定字符编码的方法,此处编码的设置传递会丢失。(PS:MultipartInputImpl中的defaultPartCharset,可以通过拦截器request.setAttribute(InputPart.DEFAULT_CHARSET_PROPERTY, ENCODING_UTF_8);进行指定)。

后续未指定字符编码的调用链中,apache-mime4j对上传内容的解析采用了默认的ASCII编码进行处理,对应RawField.parseBody()

解决resteasy上传表单文件名乱码第7张解决resteasy上传表单文件名乱码第8张
 privateString parseBody() {
        int offset = colonIdx + 1;
        int length = raw.length() -offset;
        returnContentUtil.decode(raw, offset, length);
    }
View Code

该decode方法中使用的是写死的ASCII编码进行处理

解决resteasy上传表单文件名乱码第9张解决resteasy上传表单文件名乱码第10张
public static String decode(ByteSequence byteSequence, intoffset,
            intlength) {
        returndecode(CharsetUtil.US_ASCII, byteSequence, offset, length);
    }
View Code

所以看到这里,就了解了为什么文件名称会是乱码的了,大概也知道其他地方通过拦截器设置编码格式解决不了文件名称乱码的问题了。

所以可行的解决方法可以是(亲测可用),将apache-mime4j-1.6的源码导入工程中,并且修改ContentUtil的decode方法,如下:

解决resteasy上传表单文件名乱码第11张

public static String decode(ByteSequence byteSequence, intoffset,
            intlength) {
        return decode(CharsetUtil.UTF_8 //修改此处默认编码
, byteSequence, offset, length);
    }    

这种方法不好的点就是冗余了一份开源代码到自己项目中,并且项目包路径会比较奇怪。当然也可以编译一份修改后的代码放到自己公司的nexus库中。

免责声明:文章转载自《解决resteasy上传表单文件名乱码》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇19个PHP模板引擎Verilog HDL的程序结构及其描述下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章