This article mainly introduces the knowledge of webwork file upload and download from three aspects, including the following three aspects:
1. Packaging Request request
2. Get the parsing class for file upload
3. Actual project configuration and use
Web uploading and downloading should be a very common requirement, whether it is a small website or a trading website with large concurrent access. Of course, WebWork also provides a very friendly interceptor to implement file upload, allowing us to focus on the design and implementation of business logic. When implementing upload and download, we also pay attention to the implementation of framework upload and download.
1. Packaging Request request
•Every time the client requests Action, the WebWork dispatch class ServletDispatcher.service() method will be called.
Please refer to the specific process: Detailed explanation of the method of calling Action in Webwork
public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException { try { if (encoding != null) { try { request.setCharacterEncoding(encoding); } catch (Exception localException) { } } if (locale != null) { response.setLocale(locale); } if (this.paramsWorkaroundEnabled) { request.getParameter("foo"); } request = wrapRequest(request); //封装 request请求 serviceAction(request, response, getNameSpace(request), getActionName(request), getRequestMap(request), getParameterMap(request), getSessionMap(request), getApplicationMap()); } catch (IOException e) { String message = "Could not wrap servlet request with MultipartRequestWrapper!"; log.error(message, e); sendError(request, response, 500, new ServletException(message, e)); } }
Let’s first take a look at what the wrapRequest method does:
protected HttpServletRequest wrapRequest(HttpServletRequest request) throws IOException { if ((request instanceof MultiPartRequestWrapper)) { return request; } if (MultiPartRequest.isMultiPart(request)) { request = new MultiPartRequestWrapper(request, getSaveDir(), getMaxSize()); } return request; }
• First, it determines whether the incoming request has been encapsulated by MultiPartRequestWrapper. If so, it returns directly.
• Otherwise, determine whether the ContentType in the header information of the request is a multi-type parameter (multipart/formdata) request. If so, wrap the request into WebWork's own MultiPartRequestWrapper type, which inherits HttpServletRequestWrapper.
MultiPartRequest.isMultiPart() method is implemented as follows:
public static boolean isMultiPart(HttpServletRequest request){ String content_type = request.getHeader("Content-Type"); return content_type != null && content_type.startsWith("multipart/form-data"); }
•Configuring the temporary storage directory of files and the maximum upload size in webwork.properties actually comes into play at this time.
•The parameters passed in when creating the MultiPartRequestWrapper object are obtained by the getSaveDir() and getMaxSize() methods.
•In the method, the webwork.multipart.saveDir and webwork.multipart.maxSize attributes in the configuration will be searched. If found, the temporary directory specified by the attribute and the maximum uploaded content will be used. If not found, the temporary directory of the environment will be used.
The specific implementation is as follows:
protected String getSaveDir() { String saveDir = Configuration.getString("webwork.multipart.saveDir").trim(); if (saveDir.equals("")) { File tempdir = (File) getServletConfig().getServletContext().getAttribute("javax.servlet.context.tempdir"); log.info("Unable to find 'webwork.multipart.saveDir' property setting. Defaulting to javax.servlet.context.tempdir"); if (tempdir != null) { saveDir = tempdir.toString(); } } else { File multipartSaveDir = new File(saveDir); if (!multipartSaveDir.exists()) { multipartSaveDir.mkdir(); } } if (log.isDebugEnabled()) { log.debug("saveDir=" + saveDir); } return saveDir; }
2. Get the parsing class for file upload
Let’s take a look at how the constructor of MultiPartRequestWrapper wraps request:
public MultiPartRequestWrapper(HttpServletRequest request, String saveDir, int maxSize) throws IOException { super(request); if ((request instanceof MultiPartRequest)) { this.multi = ((MultiPartRequest) request); } else { String parser = ""; parser = Configuration.getString("webwork.multipart.parser"); if (parser.equals("")) { log.warn("Property webwork.multipart.parser not set. Using com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest"); parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest"; } else if (parser.equals("pell")) { parser = "com.opensymphony.webwork.dispatcher.multipart.PellMultiPartRequest"; } else if (parser.equals("cos")) { parser = "com.opensymphony.webwork.dispatcher.multipart.CosMultiPartRequest"; } else if (parser.equals("jakarta")) { parser = "com.opensymphony.webwork.dispatcher.multipart.JakartaMultiPartRequest"; } try { Class baseClazz = MultiPartRequest.class; Class clazz = Class.forName(parser); if (!baseClazz.isAssignableFrom(clazz)) { addError("Class '" + parser + "' does not extend MultiPartRequest"); return; } Constructor ctor = clazz.getDeclaredConstructor(new Class[] { Class.forName("javax.servlet.http.HttpServletRequest"), String.class, Integer.TYPE }); Object[] parms = { request, saveDir, new Integer(maxSize) }; this.multi = ((MultiPartRequest) ctor.newInstance(parms)); } catch (ClassNotFoundException e) { addError("Class: " + parser + " not found."); } catch (NoSuchMethodException e) { addError("Constructor error for " + parser + ": " + e); } catch (InstantiationException e) { addError("Error instantiating " + parser + ": " + e); } catch (IllegalAccessException e) { addError("Access errror for " + parser + ": " + e); } catch (InvocationTargetException e) { addError(e.getTargetException().toString()); } } }
• First, it determines whether the incoming request is a subclass of the MultiPartRequest abstract class. If it is, it directly refers to the request by its own MultiPartRequest type variable.
• Otherwise, read the webwork.multipart.parser property of WebWork configuration, which determines what Webwork uses internally to implement file upload. If not specified, the implementation of PellMultiPartRequest is used by default.
•After finding the configured class, WebWork creates an instance of that class via Java reflection. All supported classes inherit from MultiPartRequest. After creating the instance, they are cast upward and assigned to the member multi of MultiPartRequestWrapper.
• WebWork's file upload encapsulates several common FileUpload libs and is not implemented by itself.
•It includes three implementations: pell, cos, and apache common. WebWork encapsulates these three packages and provides a common access interface MultiPartRequest. The detailed implementations are PellMultiPartRequest, CosMultiPartRequest, and JakartaMultiPartRequest.
• jakarta supports multiple files using the same HTTP parameter name. If you use WebWork's FileUpload interceptor directly, it is recommended to use pell, because when you upload a file with a Chinese file name, only the pell package will correctly obtain the Chinese file name, and apache common will change the file name to a file like xxx.tmp name, and cos will be garbled, so our only option is pell.
•The function of cos is relatively powerful, but the encapsulation of WebWork makes it lose a lot of functions. cos needs to set the character encoding of the request. The encapsulation of WebWork is not set, which leads to the garbled problem of cos. Of course, if you use cos alone, you will avoid such problems.
3. Actual project configuration and use
• Configuration files
action configuration:
<action name="uploadAttach" class=".....attach.action.uploadAttach" caption="上传附件"> <result name="success" type="dispatcher"> <param name="location">/result.jsp</param> </result> <result name="error" type="dispatcher"> <param name="location">/result.jsp</param> </result> <interceptor-ref name="defaultStack" /> <interceptor-ref name="fileUploadStack" /> //webwok 上传所需要的拦截栈 </action> //拦截栈的定义 <interceptor-stack name="fileUploadStack"> <interceptor-ref name="fileUpload"/> <interceptor-ref name="params"/> </interceptor-stack> //拦截栈对应的拦截器 <interceptor name="fileUpload" class="com.opensymphony.webwork.interceptor.FileUploadInterceptor"/> <interceptor name="params" class="com.opensymphony.xwork.interceptor.ParametersInterceptor"/>
•The front-end uses Ajaxupload, which is relatively stable and powerful. I won’t go into details here. There is an official website: jQuery AjaxUpload image upload code
•By encapsulating the Webwork upload request and obtaining the parsing class, all the foreplay has been prepared. The specific implementation in the upload interceptor is as follows:
public String intercept(ActionInvocation invocation) throws Exception {if (!(ServletActionContext.getRequest() instanceof MultiPartRequestWrapper)) { if (log.isDebugEnabled()) { log.debug("bypass " + invocation.getProxy().getNamespace() + "/" + invocation.getProxy().getActionName()); } return invocation.invoke(); } Action action = invocation.getAction(); ValidationAware validation = null; if ((action instanceof ValidationAware)) { validation = (ValidationAware) action; } MultiPartRequestWrapper multiWrapper = (MultiPartRequestWrapper) ServletActionContext.getRequest(); if (multiWrapper.hasErrors()) { Collection errors = multiWrapper.getErrors(); Iterator i = errors.iterator(); while (i.hasNext()) { String error = (String) i.next(); if (validation != null) { validation.addActionError(error); } log.error(error); } } Enumeration e = multiWrapper.getFileParameterNames(); while ((e != null) && (e.hasMoreElements())) { String inputName = (String) e.nextElement(); String[] contentType = multiWrapper.getContentTypes(inputName); String[] fileName = multiWrapper.getFileNames(inputName); File[] file = multiWrapper.getFiles(inputName); if (file != null) { for (int i = 0; i < file.length; i++) { log.info("file " + inputName + " " + contentType[i] + " " + fileName[i] + " " + file[i]); } } if (file == null) { if (validation != null) { validation.addFieldError(inputName, "Could not upload file(s). Perhaps it is too large?"); } log.error("Error uploading: " + fileName); } else { invocation.getInvocationContext().getParameters().put(inputName, file); invocation.getInvocationContext().getParameters().put(inputName + "ContentType", contentType); invocation.getInvocationContext().getParameters().put(inputName + "FileName", fileName); } } String result = invocation.invoke(); for (Enumeration e1 = multiWrapper.getFileParameterNames(); e1 != null && e1.hasMoreElements();) { String inputValue = (String) e1.nextElement(); File file[] = multiWrapper.getFiles(inputValue); for (int i = 0; i < file.length; i++) { File f = file[i]; log.info("removing file " + inputValue + " " + f); if (f != null && f.isFile()) f.delete(); } } return result; }
•First determine whether the current request contains a multimedia request, if so, record the log and execute the action.
•Then determine whether the MultiPartRequestWrapper contains errors during the file upload process, return the error information to the client, and do not continue to call Action.
•If none of the above judgment conditions are met, start traversing the parameters of the uploaded file in MultiPartRequestWrapper, and put the file name and file content type into the Action parameter map for subsequent business classes to operate.
•In the fileupload interceptor function of WebWork, the File it provides is only a temporary file, which will be automatically deleted after the Action is executed. You must handle the storage of the file yourself in the Action, or write it to a directory on the server, or Save to database. If you plan to write to a directory on the server, you must face the problem of dealing with the same name of the file yourself, but in fact the cos package already provides automatic renaming rules for file renames.
The above code introduces you to the relevant knowledge of Webwork for file upload and download. I hope it will be helpful to you.