<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
之前是采用上面两个第三方jar包来完成文件上传工作的,代码相对比较繁琐;无意间看了片文章说servlet 3.0支持文件上传,于是就尝试了下;先来看servlet3.0提供的几种方法,而后进入实操:
1.HttpServletRequest提供的方法
Part getPart(String name):根据名称获取文件上传域
Collection<Part> getParts():获取所有的文件上传域
2.Part中常用的方法
String getContentType():获取上传文件的文件类型
long getSize():上传文件的大小
String getSubmittedFileName():上传文件的原始文件名
String getName():获取<input name="upload" ...>标签中name属性值
String getHeader(String name):获取请求头部
Collection<String> getHeaderNames():获取所有请求头部名称
InputStream getInputStream():获取上传文件的输入流
void write(String path):保存文件至服务器
3.表单enctype属性说明
在使用标签时,需要设置enctype=“multipart/form-data”,指定表单数据的编码方式。enctype属性值说明:
application/x-www-form-urlencoded:默认编码方式,只处理表单中的value属性值,这种编码方式会将表单中的值处理成URL编码方式
multipart/form-data:以二进制流的方式处理表单数据
text/plain:当表单action属性为mailto:URL形式时比较方便,适用于直接通过表单发送邮件方式
4.@MultipartConfig注解属性说明 : 声明后的Servlet,既可以实现单个文件上传,也可以实现多个文件上传,同时还支持获取表单中的其他指字段.
属性 类型 是否必需
maxFileSize long 否
maxRequestSize long 否
fileSizeThreshold int 否
location String 否
========================
下面进入实操
- 前端html部分
<form action="../test" method="post" enctype="multipart/form-data">
分类名称:<input type="text" id="categoryName" name="categoryName" class="form-control"/>
分类图片:<input type="file" accept="image/*" id="categoryPic" name="filePath" />
<input type="submit" id="submit" value="上传图片" class="btn btn-success">
</form>
- js部分
function uploadClick() {
$("#submit").click(function() {
// 分类名称,去除首位空格后的为空判断();
var categoryName = $("#categoryName").val();
categoryName = $.trim(categoryName); // 去除首位空格;
if (1 > categoryName.length) {
alert("请输入分类名称后再提交!")
return false;
}
// 上传文件未选择判断
if (1 > $("#categoryPic").val().length) {
alert("请选择文件后,再提交!");
return false;
}
// 文件大小限制;
var categoryPicSize = $("#categoryPic")[0].files[0].size
if (500 < categoryPicSize) {
alert("上传文件大小超过500k!");
return false;
}
})
- servlet部分
@MultipartConfig(maxFileSize = 500, fileSizeThreshold = 1024 * 1024)
public class TestUploadServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
String savePath = req.getServletContext().getRealPath("img/category");
{
File addDir = new File(savePath);
if (!addDir.exists()) {
addDir.mkdirs();
}
}
String fileName = System.currentTimeMillis() + ".jpg";
try {
Part part = req.getPart("filePath"); // // 获取part对象;
part.write(savePath + File.separator + fileName);
// 文件格式转换
String fileType = part.getSubmittedFileName();
if (!fileType.endsWith(".jpg")) {
File file = new File(savePath, fileName);
BufferedImage img = ImageUtil.change2jpg(file);
ImageIO.write(img, "jpg", file);
// 文件尺寸调整
ImageUtil.resizeImage(file, 50, 50, file);
}
} catch (IOException e) {
e.printStackTrace();
} catch (ServletException e) {
e.printStackTrace();
}
- Util图片工具类
package util;
import java.awt.Image;
import java.awt.Toolkit;
import java.awt.image.BufferedImage;
import java.awt.image.ColorModel;
import java.awt.image.DataBuffer;
import java.awt.image.DataBufferInt;
import java.awt.image.DirectColorModel;
import java.awt.image.PixelGrabber;
import java.awt.image.Raster;
import java.awt.image.RenderedImage;
import java.awt.image.WritableRaster;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
public class ImageUtil {
public static BufferedImage change2jpg(File f) {
try {
Image i = Toolkit.getDefaultToolkit().createImage(f.getAbsolutePath());
PixelGrabber pg = new PixelGrabber(i, 0, 0, -1, -1, true);
pg.grabPixels();
int width = pg.getWidth(), height = pg.getHeight();
final int[] RGB_MASKS = {0xFF0000, 0xFF00, 0xFF};
final ColorModel RGB_OPAQUE = new DirectColorModel(32, RGB_MASKS[0], RGB_MASKS[1], RGB_MASKS[2]);
DataBuffer buffer = new DataBufferInt((int[]) pg.getPixels(), pg.getWidth() * pg.getHeight());
WritableRaster raster = Raster.createPackedRaster(buffer, width, height, width, RGB_MASKS, null);
BufferedImage img = new BufferedImage(RGB_OPAQUE, raster, false, null);
return img;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
}
public static void resizeImage(File srcFile, int width, int height, File destFile) {
try {
Image i = ImageIO.read(srcFile);
i = resizeImage(i, width, height);
ImageIO.write((RenderedImage) i, "jpg", destFile);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static Image resizeImage(Image srcImage, int width, int height) {
try {
BufferedImage buffImg = null;
buffImg = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
buffImg.getGraphics().drawImage(srcImage.getScaledInstance(width, height, Image.SCALE_SMOOTH), 0, 0, null);
return buffImg;
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
新手上路 , 如果有更好或不妥的地方,请多多指教 !
应用总结 ;
-
父类是servlet,重写service方法(内含文件上传功能实现代码);在req子类时,完成了"文件上传功能";
-
当把父类改为abstract抽象类,功能一样实现;注:同上,父类可以不注解@MultipartConfig;子类必须注解@MultipartConfig;
-
当前端输入流是:enctype=“multipart/form-data”,采用req.getParameter()和req.getPart()方法,能正常使用;
-
前端不是带上传功能(multipart/form-data)的混合流时,会抛异常;
-
父类通过反射,调用子类方法(获取req的getParameter和getPart),运行正常;
=======================
其他,碰到个问题,get不到相应数据,情况是:
- 如果不经过filter映射到相应servlet的method;使用是正常的;
- 否则,get不到相应数据,为null;(在filter中getParameter()就已经是null);
待解决:
J2EE下做的,filter映射到相应servlet的method;搞不定,最后还是选择采用文章开头提到的第三方类,来完成的;
注: 这个问题可能是出在:一个servlet只能转发或者重定向一次上,回头继续排查问题,同时尝试用添加接口来解决问题;待续…/ 另外,为什么filter里读取是null,也有待排查原因(我在其他类似结构的测试类中,filter中是能获取到值的,这个项目中为什么会是null???)
问题已找到: 前端发起的请求servlet名字,如admin_category_add(由于进行了servlet的映射,所以admin_category_add这个servlet实际是不存在的).所以在filter监听时,采用servlet3.0上传文件的方法,get相应值均为null;
解决方法:
1. 采用之前的第三方类,来完成相应操作;
2. 如果做个upload类,请求指向这个servlet,而后进行转发,应该也能实现类似功能;
总结:get相应值出现null的可能情况:
- 没有注解;
- 前端请求的servlet不存在;
- 超出注解配置的文件大小等限制;
其他 : 重新部署后,上传的文件会丢失;因为:重新部署项目的时候,tomcat下的webapps文件夹下的项目会被重新部署,覆盖掉了之前的项目文件,所以文件就消失了。[对策]将上传文件夹,设置在webapps文件夹外面;
========================
寻找问题时,看到其它一些较好的知识点:
无论是RequestDispatcher.forward方法,还是HttpServletResponse.sendRedirect方法,在调用它们之前,都不能有内容已经被实际输出到了客户端。如果缓冲区中已经有了一些内容,这些内容将被从缓冲区中清除。
代码的执行:
无论是 request.getRequestDispatcher(path).forward(request, response)还是response.sendRedirect,程序都会在执行完该句的情况下继续向下执行,因此在必要的时候应该使用return终止该方法.
对于 request.getRequestDispatcher(path).forward(request, response),在执行完该方法的时候再进行对request的操作已经没有任何意义,如果在该方法之后再进行request.setAttribute(),该值将不会被放进当前请求的request中.
response.setRedirect:该方法执行之后,接下来的方法也会被执行.但是使用该方法的时候,会发送一个全新的request,将不再使用原先的request,因此不论在该方法执行之前,还是在该方法执行之后,对request操作,都是无效的.