您现在的位置是:首页>漫生活>详细内容
struts2结合common.fileUpload实现文件带进度条上传
发布时间:2019-10-09 11:34:28 编辑:miki浏览(1304)评论(3)
最后,项目还残存的问题就是:明明是每隔500ms向后台请求的进度信息,但是实际上后台并不能够在这段时间内返回对应的信息。亟待优化!
效果图如下:
获取文件上传信息的基本步骤:经测试有效
上传文件时如何获取上传进度信息?
从网上查一下资料就可以知道struts2对文件上传的request请求做了封装,也就是说在调用上传文件的action时就已经上传temp完成了。
那么既然这样,我们就可以重写struts2封装request的类:org.apache.struts2.dispatcher.multipart.JakartaMultiPartRequest;
由于这个类的许多方法都是private的,那么也就是说我们是无法通过extends来重写父类的一些方法的。怎么办呢?
暴力一点!
直接自定义一个JakartaMultiPartRequest类型的类,把父类的方法全部copy过来,然后在struts.xml中配置:
....
接下来我们实现上述配置文件中的MultiPartRequest类:
通过intellij的反编译插件,我们decode JakartaMultiPartRequest这个类:源码如下—-大量代码来袭,直接copy这部分吧 不用细看
/* * $Id: JakartaMultiPartRequest.java 1384107 2012-09-12 20:14:23Z lukaszlenart $ * * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * KIND, either express or implied. See the License for the * specific language governing permissions and limitations * under the License. */ package org.apache.struts2.dispatcher.multipart; import com.opensymphony.xwork2.LocaleProvider; import com.opensymphony.xwork2.inject.Inject; import com.opensymphony.xwork2.util.LocalizedTextUtil; import com.opensymphony.xwork2.util.logging.Logger; import com.opensymphony.xwork2.util.logging.LoggerFactory; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileUploadBase; import org.apache.commons.fileupload.FileUploadException; import org.apache.commons.fileupload.RequestContext; import org.apache.commons.fileupload.disk.DiskFileItem; import org.apache.commons.fileupload.disk.DiskFileItemFactory; import org.apache.commons.fileupload.servlet.ServletFileUpload; import org.apache.struts2.StrutsConstants; import javax.servlet.http.HttpServletRequest; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.UnsupportedEncodingException; import java.util.ArrayList; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; /** * Multipart form data request adapter for Jakarta Commons Fileupload package. */ public class JakartaMultiPartRequest implements MultiPartRequest { static final Logger LOG = LoggerFactory.getLogger(JakartaMultiPartRequest.class); // maps parameter name -> List of FileItem objects protected Map> files = new HashMap>(); // maps parameter name -> List of param values protected Map> params = new HashMap>(); // any errors while processing this request protected List errors = new ArrayList(); protected long maxSize; private Locale defaultLocale = Locale.ENGLISH; @Inject(StrutsConstants.STRUTS_MULTIPART_MAXSIZE) public void setMaxSize(String maxSize) { this.maxSize = Long.parseLong(maxSize); } @Inject public void setLocaleProvider(LocaleProvider provider) { defaultLocale = provider.getLocale(); } /** * Creates a new request wrapper to handle multi-part data using methods adapted from Jason Pell's * multipart classes (see class description). * * @param saveDir the directory to save off the file * @param request the request containing the multipart * @throws java.io.IOException is thrown if encoding fails. */ public void parse(HttpServletRequest request, String saveDir) throws IOException { try { setLocale(request); processUpload(request, saveDir); } catch (FileUploadBase.SizeLimitExceededException e) { if (LOG.isWarnEnabled()) { LOG.warn("Request exceeded size limit!", e); } String errorMessage = buildErrorMessage(e, new Object[]{e.getPermittedSize(), e.getActualSize()}); if (!errors.contains(errorMessage)) { errors.add(errorMessage); } } catch (Exception e) { if (LOG.isWarnEnabled()) { LOG.warn("Unable to parse request", e); } String errorMessage = buildErrorMessage(e, new Object[]{}); if (!errors.contains(errorMessage)) { errors.add(errorMessage); } } } protected void setLocale(HttpServletRequest request) { if (defaultLocale == null) { defaultLocale = request.getLocale(); } } protected String buildErrorMessage(Throwable e, Object[] args) { String errorKey = "struts.messages.upload.error." + e.getClass().getSimpleName(); if (LOG.isDebugEnabled()) { LOG.debug("Preparing error message for key: [#0]", errorKey); } return LocalizedTextUtil.findText(this.getClass(), errorKey, defaultLocale, e.getMessage(), args); } private void processUpload(HttpServletRequest request, String saveDir) throws FileUploadException, UnsupportedEncodingException { for (FileItem item : parseRequest(request, saveDir)) { if (LOG.isDebugEnabled()) { LOG.debug("Found item " + item.getFieldName()); } if (item.isFormField()) { processNormalFormField(item, request.getCharacterEncoding()); } else { processFileField(item); } } } private void processFileField(FileItem item) { if (LOG.isDebugEnabled()) { 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()); return; } List values; if (files.get(item.getFieldName()) != null) { values = files.get(item.getFieldName()); } else { values = new ArrayList(); } values.add(item); files.put(item.getFieldName(), values); } private void processNormalFormField(FileItem item, String charset) throws UnsupportedEncodingException { if (LOG.isDebugEnabled()) { LOG.debug("Item is a normal form field"); } List values; if (params.get(item.getFieldName()) != null) { values = params.get(item.getFieldName()); } else { values = new ArrayList(); } // 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) if (charset != null) { values.add(item.getString(charset)); } else { values.add(item.getString()); } params.put(item.getFieldName(), values); item.delete(); } private List parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException { DiskFileItemFactory fac = createDiskFileItemFactory(saveDir); ServletFileUpload upload = new ServletFileUpload(fac); upload.setSizeMax(maxSize); return upload.parseRequest(createRequestContext(servletRequest)); } private DiskFileItemFactory createDiskFileItemFactory(String saveDir) { DiskFileItemFactory fac = new DiskFileItemFactory(); // Make sure that the data is written to file fac.setSizeThreshold(0); if (saveDir != null) { fac.setRepository(new File(saveDir)); } return fac; } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileParameterNames() */ public Enumeration getFileParameterNames() { return Collections.enumeration(files.keySet()); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getContentType(java.lang.String) */ public String[] getContentType(String fieldName) { List items = files.get(fieldName); if (items == null) { return null; } List contentTypes = new ArrayList(items.size()); for (FileItem fileItem : items) { contentTypes.add(fileItem.getContentType()); } return contentTypes.toArray(new String[contentTypes.size()]); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFile(java.lang.String) */ public File[] getFile(String fieldName) { List items = files.get(fieldName); if (items == null) { return null; } List fileList = new ArrayList(items.size()); for (FileItem fileItem : items) { File storeLocation = ((DiskFileItem) fileItem).getStoreLocation(); if (fileItem.isInMemory() &;&; storeLocation != null &;&; !storeLocation.exists()) { try { storeLocation.createNewFile(); } catch (IOException e) { if (LOG.isErrorEnabled()) { LOG.error("Cannot write uploaded empty file to disk: " + storeLocation.getAbsolutePath(), e); } } } fileList.add(storeLocation); } return fileList.toArray(new File[fileList.size()]); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFileNames(java.lang.String) */ public String[] getFileNames(String fieldName) { List items = files.get(fieldName); if (items == null) { return null; } List fileNames = new ArrayList(items.size()); for (FileItem fileItem : items) { fileNames.add(getCanonicalName(fileItem.getName())); } return fileNames.toArray(new String[fileNames.size()]); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getFilesystemName(java.lang.String) */ public String[] getFilesystemName(String fieldName) { List items = files.get(fieldName); if (items == null) { return null; } List fileNames = new ArrayList(items.size()); for (FileItem fileItem : items) { fileNames.add(((DiskFileItem) fileItem).getStoreLocation().getName()); } return fileNames.toArray(new String[fileNames.size()]); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameter(java.lang.String) */ public String getParameter(String name) { List v = params.get(name); if (v != null &;&; v.size() > 0) { return v.get(0); } return null; } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterNames() */ public Enumeration getParameterNames() { return Collections.enumeration(params.keySet()); } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getParameterValues(java.lang.String) */ public String[] getParameterValues(String name) { List v = params.get(name); if (v != null &;&; v.size() > 0) { return v.toArray(new String[v.size()]); } return null; } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#getErrors() */ public List getErrors() { return errors; } /** * Returns the canonical name of the given file. * * @param filename the given file * @return the canonical name of the given file */ private String getCanonicalName(String filename) { int forwardSlash = filename.lastIndexOf("/"); int backwardSlash = filename.lastIndexOf("\\"); if (forwardSlash != -1 &;&; forwardSlash > backwardSlash) { filename = filename.substring(forwardSlash + 1, filename.length()); } else if (backwardSlash != -1 &;&; backwardSlash >= forwardSlash) { filename = filename.substring(backwardSlash + 1, filename.length()); } return filename; } /** * Creates a RequestContext needed by Jakarta Commons Upload. * * @param req the request. * @return a new request context. */ private RequestContext createRequestContext(final HttpServletRequest req) { return new RequestContext() { public String getCharacterEncoding() { return req.getCharacterEncoding(); } public String getContentType() { return req.getContentType(); } public int getContentLength() { return req.getContentLength(); } public InputStream getInputStream() throws IOException { InputStream in = req.getInputStream(); if (in == null) { throw new IOException("Missing content in the request"); } return req.getInputStream(); } }; } /* (non-Javadoc) * @see org.apache.struts2.dispatcher.multipart.MultiPartRequest#cleanUp() */ public void cleanUp() { Set names = files.keySet(); for (String name : names) { List items = files.get(name); for (FileItem item : items) { if (LOG.isDebugEnabled()) { String msg = LocalizedTextUtil.findText(this.getClass(), "struts.messages.removing.file", Locale.ENGLISH, "no.message.found", new Object[]{name, item}); LOG.debug(msg); } if (!item.isInMemory()) { item.delete(); } } } } }
好的,我们把上面这部分代码copy到我们自定义的public class MultiPartRequest extends JakartaMultiPartRequest {}中
复制粘贴完了之后,我们要ctrl f找到parseRequest方法。在这里,我们要设置文件上传进度监听器
/**
* 自定义的parseRequest方法
* @param servletRequest
* @param saveDir
* @return
* @throws FileUploadException
*/
private List parseRequest(HttpServletRequest servletRequest, String saveDir) throws FileUploadException {
System.out.println("调用parseRequest方法");
UploadProgressListener listener = new UploadProgressListener(servletRequest);
DiskFileItemFactory fac = createDiskFileItemFactory(saveDir);
ServletFileUpload upload = new ServletFileUpload(fac);
upload.setSizeMax(maxSize);
upload.setProgressListener(listener);
System.out.println("设置监听器成功");
return upload.parseRequest(createRequestContext(servletRequest));
}
在上面这个代码片段中可以看到:我们new了一个监听器UploadProgressListener 的实例,然后调用了ServletFileUpload .setProgressListener(listener)方法。也就是说:这里的监听器与我们以前写过的request,session等等监听器不同,不需要在web.xml中配置。这里只需要一行代码即可解决。
那么我们就来实现这个监听器吧!
这个监听器需要实org.apache.commons.fileupload.ProgressListener这个接口:我们直接implement一下。
在update方法中我们可以获取到已上传的文件长度,上传的文件的总长度,当前上传第几个文件。(当你上传一个文件的时候,打印日志就会发现这里会输出2。个人猜测是先上传到temp然后再上传到指定路径…)
我们在update方法中设置了文件上传状态实体类的实例,并且保存到session。这一步就是能让前端使用ajax访问到文件当前的上传进度信息。
package listener; import entity.FileUploadProgress; import org.apache.commons.fileupload.ProgressListener; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; /** * Created by zipple on 2017/10/13. * 监听文件上传情况 * 实现org.apache.commons.fileupload.ProgressListener接口 */
/**
* 2018.11.27 miki 监听文件上传进度的监听器
*
*/
public class UploadProgress implements ProgressListener {
HttpSession session;
/*
* 实时更新上传进度
* arg0:当前上传文件的大小
* arg1:总共需要上传的大小
* arg2:剩余上传文件的大小
*/
public void update(long arg0, long arg1, int arg2) {
// TODO Auto-generated method stub
if(arg1==0L){
return;
}
//创造json格式参数
String value="var info={read:"+arg0+",total:"+arg1+",items:"+1+"}"; //items:"+arg2+" d多文件上传的时候,使用arg2
// System.out.println(value);
session.setAttribute("progress", value); //上传完毕后,记得清除上传过后的进度条session
}
public UploadProgress(HttpSession session){
this.session=session;
}
}
前端获取后台文件上传的进度信息
上传文件可大致分为两个阶段:1. 上传到服务器上,在临时目录中 2.从临时目录中把文件移到指定目录(由自己写的action处理),而struts2.的监听器只监听
public String upload()throws Exception{
//2017.11.16 21:58 miki 加载网页静态化缓存路径
Properties prop = new Properties();
try {
prop.load(this.getClass().getClassLoader().getResourceAsStream("resource.properties"));
} catch (IOException e1) {
throw new RuntimeException(e1);
}
//2017.11.16 20:37 取静态资源中常量
// String HTML_GEN_PATH = prop.getProperty("HTML_GEN_PATH");//静态资源存储路径
HttpServletResponse response=ServletActionContext.getResponse();
HttpSession session=request.getSession();
user=(User) session.getAttribute("currentUser");
if(user==null){
request.getRequestDispatcher("error.jsp").forward(request, response);
}
System.out.println("上传文件名为:"+uploadFileName);
shop=shopService.getShopByuserId(user.getId());
// 2017.08.05 miki 文件二次命名,作为下载url链接,采用uuid加密命名,并截取2-18位字符,二次加密,增加安全性
String imageName = "file_"+RadomUtil.getUUID().substring(2, 18)+uploadFileName.substring(uploadFileName.lastIndexOf("."));
File newfile = new File(ServletActionContext.getServletContext().getRealPath("/image_upload/source_upload/file")+"\\"+imageName);
IOUtils.cp(upload, newfile); //因为要设置表单里没有的属性(setImage(),)所以重新创造了一个photo对象
//清除上传过后的进度条session
session.setAttribute("progress", null);
source.setPath(imageName);
source.setUser(user);
source.setShop(shop);
source.setUpload_time(new Date());
sourceService.save(source);
user.setScore(user.getScore()+5);
shop.setSourceNum(shop.getSourceNum()+1);
shopService.save(shop);
userService.saveUser(user);
// /*
// * 2017.11.16 20:31 网页静态化实例
// */
// try {
// User vistor=(User) session.getAttribute("currentUser");
// if (StringUtil.isEmpty(page)) {
// page="1";
// }
// PageBean pageBean=new PageBean(Integer.parseInt(page), 6);
// sourceList=sourceService.getSourcesByShopId(shop.getId(), pageBean);
// long total=sourceService.getSourceCountByuserId(shop.getUser().getId());
// StringBuffer param=new StringBuffer();
// param.append("shopId="+shop.getId());
// pageCode=PageUtil.genPagination(request.getContextPath()+"/shop/Shop_view.action", total, Integer.parseInt(page), 6,param.toString());
//
//
// //创建一个数据集,Map将data封装进去
// Map data=new HashMap<>();
// data.put("vistor", vistor);
// data.put("user", user);
// data.put("shop", shop);
// data.put("sourceList", sourceList);
// data.put("pageCode", pageCode);
// //加载模板对象
// Configuration configuration=freeMarkerConfigurer.getConfiguration();
// Template template = configuration.getTemplate("shop.ftl");
// //创建一个输出流,指定目录及文件名
// Writer out=new FileWriter(HTML_GEN_PATH +shop.getId()+ ".html");
// //生成静态页面
// template.process(data, out);
// //关闭流
// out.close();
//
// } catch (Exception e) {
// // TODO: handle exception
// e.printStackTrace();
// }
return "su";
}
返回json数据的struts.xml配置如下:
shop/Shop_upload.action
项目做到这里,后端的事情已经做完了。接下来就是前端显示进度条的事情。需要的同学可以继续往下看咯。
前端那些事儿——css实现进度条控件
css:可以直接copy,无需细看(firefox下测试无问题)
控制进度条代码:在提交文件时同步调用此计时器即可。
...
if('${shop.sourceNum}'=='${shop.maxNum}'){
$("#error").html("*您已达到资源上传上限,请升级权限再来*");
return false;
}
$("#error").html("");
createProgress();
//getProgress(); //定时刷新
setTimeout("getProgressBar()",1000);
return true;
/** * 展示上传进度条 */
关键字词:struts2;文件上传;进度条
下一篇:博客新增自定义文章分类功能啦