struts2的文件上传很容易,特别是对上传文件的2进制流解析,操作很透明。只要action有个File类型的属性给setter方法就直接把2进制文件给封装成File了。
开始的时候,我是打算在在servlet中手动解析的,后发现太繁琐要判断2进制文件流的开始和结束还有及加载其中的表单域,还要考率超大文件的内存溢出问题。TMD真不爽。后来打算借鉴一下struts2的代码。但是,struts2怎么把2进制文件变成了File了? File是通常是关联到本地操作系统上的文件,上传的文件是封装在request对象中的,怎么能封装成本地的文件,难道是先传到临时目录在写到指定目录,但考虑到性能问题应该是边传边写的啊。
不知道很不爽啊,感觉就是gf背着你和别的帅哥出去约会一样,并且约会的人是struts2团队的一个老外,想想就更不爽了。于是我就是开始看源码。struts2的上传是用拦截器实现的,打开struts2-core.jar包很容易找到org.apache.struts2.interceptor
这个包,以后瞅瞅一眼就看到FileUploadInterceptor.class这个文件了。打开相应的源码:
- /*
- *.省略导入文件
- */
- public String intercept(ActionInvocation invocation) throws Exception {
- ActionContext ac = invocation.getInvocationContext();
- HttpServletRequest request = (HttpServletRequest) ac.get(ServletActionContext.HTTP_REQUEST);
- if (!(request instanceof MultiPartRequestWrapper)) {
- if (LOG.isDebugEnabled()) {
- ActionProxy proxy = invocation.getProxy();
- LOG.debug(getTextMessage("struts.messages.bypass.request", new Object[]{proxy.getNamespace(), proxy.getActionName()}, ActionContext.getContext().getLocale()));
- }
- return invocation.invoke();
- }
- final Object action = invocation.getAction();
- ValidationAware validation = null;
- if (action instanceof ValidationAware) {
- validation = (ValidationAware) action;
- }
- MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) request;
- if (multiWrapper.hasErrors()) {
- for (Iterator errorIter = multiWrapper.getErrors().iterator(); errorIter.hasNext();) {
- String error = (String) errorIter.next();
- if (validation != null) {
- validation.addActionError(error);
- }
- LOG.error(error);
- }
- }
- Map parameters = ac.getParameters();
- // Bind allowed Files
- Enumeration fileParameterNames = multiWrapper.getFileParameterNames();
- while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
- // get the value of this input tag
- String inputName = (String) fileParameterNames.nextElement();
- // get the content type
- String[] contentType = multiWrapper.getContentTypes(inputName);
- if (isNonEmpty(contentType)) {
- // get the name of the file from the input tag
- String[] fileName = multiWrapper.getFileNames(inputName);
- if (isNonEmpty(fileName)) {
- // Get a File object for the uploaded File
- //这里这么就一下得到file了,看来是multiWrapper的问题
- File[] files = multiWrapper.getFiles(inputName);
- if (files != null) {
- for (int index = 0; index < files.length; index++) {
- if (acceptFile(files[index], contentType[index], inputName, validation, ac.getLocale())) {
- parameters.put(inputName, files);
- parameters.put(inputName + "ContentType", contentType);
- parameters.put(inputName + "FileName", fileName);
- }
- }
- }
- } else {
- LOG.error(getTextMessage("struts.messages.invalid.file", new Object[]{inputName}, ActionContext.getContext().getLocale()));
- }
- } else {
- LOG.error(getTextMessage("struts.messages.invalid.content.type", new Object[]{inputName}, ActionContext.getContext().getLocale()));
- }
- }
- // invoke action
- String result = invocation.invoke();
- // cleanup
- fileParameterNames = multiWrapper.getFileParameterNames();
- while (fileParameterNames != null && fileParameterNames.hasMoreElements()) {
- String inputValue = (String) fileParameterNames.nextElement();
- File[] file = multiWrapper.getFiles(inputValue);
- for (int index = 0; index < file.length; index++) {
- File currentFile = file[index];
- if(LOG.isInfoEnabled()) {
- LOG.info(getTextMessage("struts.messages.removing.file", new Object[]{inputValue, currentFile}, ActionContext.getContext().getLocale()));
- }
- if ((currentFile != null) && currentFile.isFile()) {
- currentFile.delete();
- }
- }
- }
- return result;
- }
- /*
- *省略一些代码
- */
- }
- public class MultiPartRequestWrapper extends StrutsRequestWrapper {
- protected static final Logger LOG = LoggerFactory.getLogger(MultiPartRequestWrapper.class);
- Collection<String> errors;
- // MultiPartRequest 是得到file的关键
- MultiPartRequest multi;
- public MultiPartRequestWrapper(MultiPartRequest multiPartRequest, HttpServletRequest request, String saveDir) {
- super(request);
- multi = multiPartRequest;
- try {
- multi.parse(request, saveDir);
- for (Object o : multi.getErrors()) {
- String error = (String) o;
- addError(error);
- }
- } catch (IOException e) {
- addError("Cannot parse request: "+e.toString());
- }
- }
- public File[] getFiles(String fieldName) {
- if (multi == null) {
- return null;
- }
- //通过multi得到File的
- return multi.getFile(fieldName);
- }
- }
- public class JakartaMultiPartRequest implements MultiPartRequest {
- static final Logger LOG = LoggerFactory.getLogger(MultiPartRequest.class);
- // maps parameter name -> List of FileItem objects
- private Map<String,List<FileItem>> files = new HashMap<String,List<FileItem>>();
- // maps parameter name -> List of param values
- private Map<String,List<String>> params = new HashMap<String,List<String>>();
- // any errors while processing this request
- private List<String> errors = new ArrayList<String>();
- private long maxSize;
- @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE)
- public void setMaxSize(String maxSize) {
- this.maxSize = Long.parseLong(maxSize);
- }
- //主要就是这个方法解析了request并封装成了File类型
- 很明显使用了commons-fileupload.jar的框架
- public void parse(HttpServletRequest servletRequest, String saveDir)
- throws IOException {
- DiskFileItemFactory fac = new DiskFileItemFactory();
- // Make sure that the data is written to file
- //控制上传文件大小的阀值设置成了0,这就会直接写到临时文件去了
- fac.setSizeThreshold(0);
- if (saveDir != null) {
- //设置临时目录
- fac.setRepository(new File(saveDir));
- }
- // Parse the request
- try {
- ServletFileUpload upload = new ServletFileUpload(fac);
- upload.setSizeMax(maxSize);
- List items = upload.parseRequest(createRequestContext(servletRequest));
- for (Object item1 : items) {
- FileItem item = (FileItem) item1;
- if (LOG.isDebugEnabled()) LOG.debug("Found item " + item.getFieldName());
- if (item.isFormField()) {
- LOG.debug("Item is a normal form field");
- List<String> values;
- if (params.get(item.getFieldName()) != null) {
- values = params.get(item.getFieldName());
- } else {
- values = new ArrayList<String>();
- }
- // note: see http://jira.opensymphony.com/browse/WW-633
- // basically, in some cases the charset may be null, so
- // we're just going to try to "other" method (no idea if this
- // will work)
- String charset = servletRequest.getCharacterEncoding();
- if (charset != null) {
- values.add(item.getString(charset));
- } else {
- values.add(item.getString());
- }
- params.put(item.getFieldName(), values);
- } else {
- LOG.debug("Item is a file upload");
- // Skip file uploads that don't have a file name - meaning that no file was selected.
- if (item.getName() == null || item.getName().trim().length() < 1) {
- LOG.debug("No file has been uploaded for the field: " + item.getFieldName());
- continue;
- }
- List<FileItem> values;
- if (files.get(item.getFieldName()) != null) {
- values = files.get(item.getFieldName());
- } else {
- values = new ArrayList<FileItem>();
- }
- values.add(item);
- //把解析出来的文件都放到这里
- files.put(item.getFieldName(), values);
- }
- }
- } catch (FileUploadException e) {
- LOG.error("Unable to parse request", e);
- errors.add(e.getMessage());
- }
- }
- //根据文件名返回File数组
- public File[] getFile(String fieldName) {
- List items = (List) files.get(fieldName);
- if (items == null) {
- return null;
- }
- List<File> fileList = new ArrayList<File>(items.size());
- for (int i = 0; i < items.size(); i++) {
- DiskFileItem fileItem = (DiskFileItem) items.get(i);
- fileList.add(fileItem.getStoreLocation());
- }
- return (File[]) fileList.toArray(new File[fileList.size()]);
- }
- }