Dubbo在2.6版本後合併了dubbox的resteasy代碼後,能夠支持rest風格的接口發佈,可是在使用form表單上傳文件的時候,獲取的文件名稱是亂碼。apache
下面經過對源碼分析一下緣由,並提供一種可行的解決方法。ide
首先是一個resteasy上傳的使用代碼函數
@POST @Path("/upload") @Consumes(MediaType.MULTIPART_FORM_DATA) @Override public Object 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 = new FileOutputStream(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) { } } } } return Response.ok().build(); }
resteasy文件上傳使用的Consumes使用的mediattype類型是MULTIPART_FORM_DATE【@Consumes(MediaType.MULTIPART_FORM_DATA)】源碼分析
這個mediatype使用的Provider使用的是org.jboss.resteasy.plugins.providers.multipart.MultipartFormDataReader,其readForm的入口爲ui
public MultipartFormDataInput readFrom(Class<MultipartFormDataInput> type, Type genericType, Annotation[] annotations, MediaType mediaType, MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException { String boundary = mediaType.getParameters().get("boundary"); if (boundary == null) throw new IOException(Messages.MESSAGES.unableToGetBoundary()); MultipartFormDataInputImpl input = new MultipartFormDataInputImpl(mediaType, workers); input.parse(entityStream); return input; }
在跟入上面代碼的parse方法 input.parse(entityStream)中的new BinaryMessage()構造函數中,MultipartInputImpl對http的head進行了解析this
private static class BinaryMessage extends Message { private BinaryMessage(InputStream is) throws IOException, MimeIOException { try { MimeStreamParser parser = new MimeStreamParser(null); StorageProvider storageProvider; if (System.getProperty(DefaultStorageProvider.DEFAULT_STORAGE_PROVIDER_PROPERTY) != null) { storageProvider = DefaultStorageProvider.getInstance(); } else { StorageProvider backend = new CustomTempFileStorageProvider(); storageProvider = new ThresholdStorageProvider(backend, 1024); } parser.setContentHandler(new BinaryOnlyMessageBuilder(this, storageProvider)); parser.parse(is); // 此處未解析代碼,未傳入指定的字符串編碼方式 } catch (MimeException e) { throw new MimeIOException(e); } } }
在行 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()spa
private String parseBody() { int offset = colonIdx + 1; int length = raw.length() - offset; return ContentUtil.decode(raw, offset, length); }
該decode方法中使用的是寫死的ASCII編碼進行處理rest
public static String decode(ByteSequence byteSequence, int offset, int length) { return decode(CharsetUtil.US_ASCII, byteSequence, offset, length); }
因此看到這裏,就瞭解了爲何文件名稱會是亂碼的了,大概也知道其餘地方經過攔截器設置編碼格式解決不了文件名稱亂碼的問題了。code
因此可行的解決方法能夠是(親測可用),將apache-mime4j-1.6的源碼導入工程中,而且修改ContentUtil的decode方法,以下:
public static String decode(ByteSequence byteSequence, int offset, int length) { return decode(CharsetUtil.UTF_8 //修改此處默認編碼 , byteSequence, offset, length); }
這種方法很差的點就是冗餘了一份開源代碼到本身項目中,而且項目包路徑會比較奇怪。固然也能夠編譯一份修改後的代碼放到本身公司的nexus庫中。