在做shopSys项目的“商品添加”功能时,需要上传所添加商品的图片,这是该功能的背景。
(注:下面所讲述的是在项目开发时ssm框架环境与配置都搭建好的情况下进行的)
1.在实现之前,需要导入两个jar包:commons-fileupload 和 commons-io
2.首先是springmvc.xml中的配置:
<!-- 上传文件 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="defaultEncoding" value="utf-8" />
<property name="maxInMemorySize" value="10240" /> <!-- 最大内存大小 -->
<property name="maxUploadSize" value="-1" /> <!-- 最大文件大小,-1为不限制大小 -->
</bean>
springmvc上传图片(文件)是通过MultipartResolver(Multipart解析器)处理的,对于MultipartResolver而言它只是一个接口, 它有两个实现类:CommonsMultipartResolver和StandardServletMultipartResolver。我用的是前者,因为它可以在spring的各个版本使用,但是需要依赖第三方包才能实现(就是上面的两个包),而后者不依赖第三方包,但是要求sping版本在3.1以上。
而我最喜欢的就是相比于jsp的文件上传与下载它就只需要配置springmvc.xml中的这一个地方,借助于该包中的类来实现。
3.然后就是前台的jsp页面:
<form action="${pageContext.request.contextPath}/addProduct.do" method="post" enctype="multipart/form-data">
商品图片<input type="file" name="file" />
</form>
需要注意的是form表单中一定要写 enctype=“multipart/form-data”,否则springmvc就会解析失败。这个的作用就是将form表单的数据以二进制的方式传输。并且method要是post方式。
而这里的name='file’属性并不是数据库的真实image列值,只是暂时存储图片的一个名称。
也就是这里我觉得最厉害的地方就是我将MultipartFile定义在了商品的实体类中,就是input中的name='file’属性:
/**
* @author Martian
* 商品表实体类
*/
public class Product {
private int pid;
private String pname;
private double market_price;
private double shop_price;
private String image;
private String pdesc;
private int is_hot;
private String pdate;
private int csid;
private MultipartFile file; //文件上传
在商品实体类中多加了一个file属性,这样对后面代码的书写提供了极大的方便。
4.最后就是Controller类的后台处理了:
/**
* @param p
* @return 添加商品
* @throws IOException
* @throws IllegalStateException
*/
@RequestMapping("/addProduct.do")
public String addProduct(Product p,HttpServletResponse resp) throws IllegalStateException, IOException {
//定义 文件名
String filename=null;
//定义文件保存的本地路径
String localPath="D:\\下载应用\\Tomcat服务(服务器)\\apache-tomcat-8.5.42\\webapps\\shopSys\\shop\\products\\1\\";
// 保存数据库的路径名称,即数据库中的image列
String sqlPath = null;
/*
* 获取前台传过来的file值,如果不为空,则执行该if里面的代码
* 需要注意的是:这里我将MultipartFile即file属性定义在了商品的实体类中。
*/
if(!p.getFile().isEmpty()){
//用uuid随机生成一个文件裸名
String uuid =UUID.randomUUID().toString().replaceAll("-","");
//获得文件类型(可以判断如果不是图片,禁止上传)
String contentType=p.getFile().getContentType();
if (!contentType.equals("image/jpeg") && !contentType.equals("image/png") && !contentType.equals("image/gif")) {
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.flush();
out.println("<script>");
out.println("alert('请上传图片文件!');");
out.println("history.back();");
out.println("</script>");
return "shop/manager/category/addProduct.jsp";
} else {
//获得文件后缀名
String suffixName=contentType.substring(contentType.indexOf("/")+1);
//得到文件名(文件名由文件裸名与后缀名组合而成)
filename=uuid+"."+suffixName;
//获取商品实体类中的file属性赋值,而file属性是MultipartFile类型的,将前台上传的该图片保存至该本地路径的文件夹中
p.getFile().transferTo(new File(localPath+filename));
}
}
//把图片的相对路径保存至数据库
sqlPath = "products/1/"+filename;
p.setImage(sqlPath); //设置商品实体类中的image属性的值
/*
* 用户上传的图片保存在相应的服务器中 即上面所定义的文件保存的本地路径localPath的文件夹中,在数据库中只保存图片的相对路径image属性的值。
*/
String format = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss").format(new Date());
p.setPdate(format);
ser.addProduct(p);
return "selectAllProduct.do";
}
5.总结:
其根本就是要搞清楚:jsp图片的上传,其实用户上传的图片是保存在相应的服务器中的(我的是保存在Tomcat服务器中相应的项目文件夹内),而在数据库中只是保存图片的相对路径image属性的值,以这两点为方向就不难搞清了。
第二就是要注意三个值:
//文件名
String filename=null;
//文件保存的本地路径
String localPath="";
//文件保存数据库的路径名称,即数据库中的image列的值
String sqlPath = null;
localPath和sqlPath就是上面说的要注意的点的值,而这两个值又取决于文件名filename,所有filename也就显得尤为重要了。
而需要注意的是 后台这里的文件名filename是自己随机生成的,与用户前台传过来的那个用户自己的图片名称没有一点关系,因为我们只需要用户的那张图片,而他的名字对我们没用,我们要保证存入数据库中的这张图片的名称是不重复的,所以需要随机生成名字后并保存入数据库,以便查询时用来显示用。
附数据库的设计:
前台页面实现结果: