话说
各位读者盆友,中午好!今日小结下文件上传
目录:
自己处理文件上传的时候,多用2种方式:
1.框架方式(SpringMVC)
2.Servlet方式
1)仅只有文件上传域
2)既有普通上传域,又有文件上传域
难度系数:★★☆☆☆
今天对这2中方式做个整体总结。
1.框架方式(SpringMVC)
用框架,一切都显得那么容易了。我们先来容易的。其实类似JDBC,文件上传也是固定的步骤。
1)导包
2)写前端
3)配置springmvc.xml
4)写方法
4.1 获取上传文件名
4.2 得到前、后缀名
4.3 创建输入、输出流(路径)
4.4 CopyFile
4.5 写入数据库
4.6 页面显示
应用场景:比如,我们添加商品的时候,需要添加商品信息和图片,那么这个需求就是:一个form表单里面,既有普通表单项,又有文件上传表单项,看起来比较简单,实际上如果用后面Servlet方式,还是有不少坑的。只是框架帮我们简化了许多。
1)导包
commons-fileupload-1.3.2.jar
commons-io-2.5.jar
文件上传,本质就是IO流的操作;加上我们要用人家文件上传的工具类,所以这2个包都是必须的。
2)写前端
<input type="file" name="file" value="添加图片" accept="image/png,image/gif,image/jpg,image/jpeg">
<form method="post" action="addGoods2" enctype="multipart/form-data">
首先搞定单个文件上传域,name很重要!这个name就是后面的MultipartFile的别名,必须一致,否则找不到。这和我们通过name得到表单value原理一致嘛。
enctype=”multipart/form-data” 这个非常重要!表明表单不是普通的表单,而是要用二进制处理(流)来处理的表单。
3)配置springmvc.xml
<!--配置文件上传 解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设置文件最大尺寸为5M -->
<property name="maxUploadSize">
<value>5242880</value>
</property>
</bean>
这里的Id必须有,属性可以视情况而定。怎么找到类全名呢?
Eclipse中:Ctrl + Shift + T 打开搜索,输入CommonsMultipartResolver即可!这也是一个解析器,类似视图解析器之类的东东。
4)写方法
4.1 获取上传文件名
4.2 得到前、后缀名
4.3 创建输入、输出流(路径)
4.4 CopyFile
4.5 写入数据库
4.6 页面显示
@RequestMapping(value="addGoods2",method=RequestMethod.POST)
public String addGoods2(Goods goods,MultipartFile file) throws Exception {
if(file != null) {
//1)获取上传文件名称
String oriName = file.getOriginalFilename();//文件名+后缀名 100.png//单纯实现效果,这样就可以了!但是在不同位置同名同后缀的图片或者文件太正常了,但是一旦存入,后者就会把前者覆盖掉;这是我们不愿意看到的,比如:很多人名字都是小明,但是是不同的个体哈。
//2)修改名称
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddhhmmss");
String picName3 = sdf.format(new Date());//还可以用另外2中方式命名:
String extName = oriName.substring(oriName.lastIndexOf("."));//获取的图片后缀名是:.jpg
FileOutputStream fos = new FileOutputStream("F:\\img5"+"\\"+"小美截图"+picName3+extName);
//3)写入本地存储路径
FileCopyUtils.copy(file.getInputStream(), fos);
//4)图片路径存入数据库
goods.setGoodsImg("小美截图"+picName3+extName);//这才是存入数据库的形式
goodsService.addGoods(goods);
}
return "redirect:goodsList";
}
/**
* 以上代码小结:
* 1.MultipartFile.getName()是获取表单项的name,也就是<input type="file" name="file" value="添加图片" >这里面的name
* 2.getOriginalFilename()获取的是你上传文件名:eg:100.png
* 3.要避免同名图片(文件)覆盖,可以自由命名 在下总结了3种方式,可以自由选择:
* 法1:UUID 1f7dc2e572844e959e4c868c92ee9b7b.png
* 法2:时间戳 1516887657528.png
* 法3:模仿QQ截图:QQ截图20180125214926.png ==》 小美截图20180125215052.png
* 具体实现方式:法1:String picName = UUID.randomUUID().toString().replace("-", "");//UUID产生的随机文件名:1f7dc2e5-7284-4e95-9e4c-868c92ee9b7b 去掉中间-后的结果是:1f7dc2e572844e959e4c868c92ee9b7b
* 法2:Long picName2 = System.currentTimeMillis();//时间戳方式命名
*
* 总结:SpringMVC实现单个文件上传核心就3步:
* 1步:FileCopyUtils.copy(inputStream, outPutStream);
* 2步:给一个输出流:完整路径名
* 3步:上传数据库
*/
--------------------------------------------------------------------------------
页面展示:用绝对路径:类似于这样的:http://192.168.11.123/配置虚拟路径名/数据库存储图片名
<%
String basePath = request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort();
%>
当然有时候会也会用到获取项目名称:request.getContextPath()直接拼接即可,这里我们的项目名称已经被我们取的img5所替代,所以用不着这个。
getScheme() ——获取协议名:http
getServerName()——获取服务器地址,也就是类似:localhost 192.168 本机IP地址
getServerPort()——端口号 8080 或者80
需要特别注意的是:我们在输出流确定路径的时候,是直接在本地磁盘某个位置写入的,实际工作中直接写入服务器某个位置:要做如下配置:图形化界面配置或者修改配置文件都可以。
法1:图形化界面
DocumentBase 就是配置文件、图片实际上传到本地的位置,自己定义
Path就是抽象后的路径,项目中就用path来访问即可。
每次手动配置过后,自动会在Servler.xml中增加配置,和配置Servler.xml本质相同!
法2:修改配置文件
<Context docBase="store" path="/store" reloadable="true" source="org.eclipse.jst.jee.server:store"/>
<Context docBase="F:\img3" path="/img3" reloadable="true"/>
<Context docBase="F:\img4" path="/img4" reloadable="true"/>
docBase表示图片真实存放在本地的位置;path表示项目中用这个去实际访问。
页面上这么 访问即可。
<img src="<%=basePath2 %>/img5/${goods.goodsImg }" style="width:50px;height:50px">
2.Servlet方式
上面操作简单,下面Servlet的方式是入门级的方式,当然配置路径方法和上面相同,重点在于使用手段有差别。分为2中情况。
1)仅只有文件上传域
2)既有普通上传域,又有文件上传域
步骤和上面类似:
导包==》页面表单enctype=”multipart/form-data” ==>写方法 因为类似,省略。只展示核心代码。
1)仅只有文件上传域
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
User user = (User)req.getSession().getAttribute("user");
int userId = user.getUserId();
//1.判断是否是文件上传域表单
boolean isMultiPart = ServletFileUpload.isMultipartContent(req);
if(isMultiPart) {
//2.是的话,穿件文件上传对象ServletFileUpload
ServletFileUpload sfu = new ServletFileUpload(new DiskFileItemFactory());
try {
//3.处理form表单提交过来的请求
List<FileItem> listFileItem = sfu.parseRequest(req);
//4.遍历每单个表单项,判断是否是文件上传表单项
for(FileItem item:listFileItem) {
//如果不是普通的表单域(是文件上传域)
if(!item.isFormField()) {
//5.获取上传图片(文件)名称
String fileName = item.getName();
System.out.println("文件名:"+fileName);//文件名:100.png
String path = "F:\\img2";
File file = new File(path);
//6.穿件路径
String path2 = file + "\\"+fileName;
File file2 = new File(path2);
//7.写入
item.write(file2);
//8.把路径存放数据库
UserRegisterService urs = new UserRegisterServiceImpl();
String sql = "update user set fileName = ? where userId = ?";
Object[] params = {fileName,userId};
urs.Cud(sql, params);
//修改一下session
user.setFileName(fileName);
}
}
//跳转页面
req.getRequestDispatcher("editUserInfo.jsp").forward(req, resp);
} catch (FileUploadException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
通过上面可以发现,尤其是路径那部分,每次都要File new File很是麻烦,其他注意点和SpringMVC差不多了。
2)既有普通上传域,又有文件上传域
如果我们用ServletFileUpload来处理的话,一旦遇到这种情况,比如这个方法:
页面是这样的:
//2.2执行 新增操作
@RequestMapping(value="addGoods",method=RequestMethod.POST)
public String addGoods(Goods goods,HttpServletRequest req) throws Exception {
String fileName = "";
//上传图片
//凭借商品属性值Id
String attrValIds = "";
boolean isMultiForm = ServletFileUpload.isMultipartContent(req);
这样存在什么问题?那就是页面传过来的goods整个对象没发直接默认接收,因为全部变成了二进制流!所以,如果是一个对象,那么这个对象所有的属性需要重新接收一遍,否则整个对象获取不到。
这个时候,代码是这样的:
//2.2执行 新增操作
@RequestMapping(value="addGoods",method=RequestMethod.POST)
public String addGoods(Goods goods,HttpServletRequest req) throws Exception {
String fileName = "";
//上传图片
//凭借商品属性值Id
String attrValIds = "";
boolean isMultiForm = ServletFileUpload.isMultipartContent(req);
if(isMultiForm) {
System.out.println("获取商品图片路径:进来了!!!");
ServletFileUpload sfu = new ServletFileUpload(new DiskFileItemFactory());
List<FileItem> fileItemsList = sfu.parseRequest(req);
for(FileItem fileItem : fileItemsList) {
if(!fileItem.isFormField()) {
//写上传图片代码
String path="F:\\img4";
File file = new File(path);
fileName = fileItem.getName();
System.out.println("上传图片名字是:"+fileName);
String path2 = file + "/"+fileName;
File file2 = new File(path2);
fileItem.write(file2);
goods.setGoodsImg(fileName);
System.out.println("赋值后的数据库商品路径:"+goods.getGoodsImg());
}else {
if("goodsName".equals(fileItem.getFieldName())) {
String goodsName = fileItem.getString("utf-8").trim();
goods.setGoodsName(goodsName);
System.out.println("获取到的商品名称为======================:"+goodsName);
}
if("attributeValId".equals(fileItem.getFieldName())) {
String attributeValId = fileItem.getString();
System.out.println("获取到的属性值Id数组为:======================:"+attributeValId);
attrValIds +=attributeValId+",";
}
if("categoryId".equals(fileItem.getFieldName())) {
String categoryId = fileItem.getString();
System.out.println("获取到的分类Id为:======================:"+categoryId);
if(categoryId != null && categoryId != "") {
goods.setCategoryId(Integer.parseInt(categoryId));
}
}
if("goodsTagId".equals(fileItem.getFieldName())) {
String goodsTagId = fileItem.getString();
System.out.println("获取到的商品标签Id为:======================:"+goodsTagId);
if(goodsTagId != null && goodsTagId != "") {
goods.setGoodsTagId(Integer.parseInt(goodsTagId));
}
}
//===================================================
if("goodsCount".equals(fileItem.getFieldName())) {
String goodsCount = fileItem.getString();
System.out.println("获取到的商品数量为:======================:"+goodsCount);
if(goodsCount != null && goodsCount != "") {
goods.setGoodsCount(Integer.parseInt(goodsCount));
}
}
if("goodsPrice".equals(fileItem.getFieldName())) {
String goodsPrice = fileItem.getString();
System.out.println("获取到的商品价格为:======================:"+goodsPrice);
if(goodsPrice != null && goodsPrice != "") {
goods.setGoodsPrice(Double.parseDouble(goodsPrice));
}
}
if("goodsSalePrice".equals(fileItem.getFieldName())) {
String goodsSalePrice = fileItem.getString();
//System.out.println("获取到的商品价格为:======================:"+goodsSalePrice);
if(goodsSalePrice != null && goodsSalePrice != "") {
goods.setGoodsSalePrice(Double.parseDouble(goodsSalePrice));
}
}
if("goodsUrl".equals(fileItem.getFieldName())) {
String goodsUrl = fileItem.getString("utf-8").trim();
goods.setGoodsUrl(goodsUrl);
//System.out.println("获取到的商品名称为======================:"+goodsUrl);
}
if("goodsBrief".equals(fileItem.getFieldName())) {
String goodsBrief = fileItem.getString("utf-8").trim();
goods.setGoodsBrief(goodsBrief);
//System.out.println("获取到的商品名称为======================:"+goodsBrief);
}
if("goodsDetail".equals(fileItem.getFieldName())) {
String goodsDetail = fileItem.getString("utf-8").trim();
goods.setGoodsDetail(goodsDetail);
//System.out.println("获取到的商品名称为======================:"+goodsDetail);
}
}
}
System.out.println("拼接后的属性字符串为:"+attrValIds);
goods.setAttrValIds(attrValIds);
goodsService.addGoods(goods);
}
}
return "redirect:goodsList";
}
既然都是二进制流,所以通通要按照二进制流的方式在接收每一个属性.直接通过request.getParameter()是没法接收的,因为是二进制。所以框架的整个便利都失效了,属性得一个个接收,仿佛回归到了JDBC…….你想想,这是多么“浩瀚”的工程!对比SpringMVC,真是太繁琐了!
小结:Servlet文件上传
文件上传四步走:
1.form表单
enctype=”multipart/form-data”
2.导包
commons-fileupload-1.3.2.jar commons-io-2.5.jar
3.写Servlet
核心:ServletFileUpload对象
步骤:1)判断表单是否是文件上传表单类型:ServletFileUpload.isMultipartContent(req);
2)创建ServletFileUpload对象:new ServletFileUpload(new DiskFileItemFactory());
List<FileItem> list集合。
4)判断此文件上传类型表单中的表单项是否是文件上传项:是的话,在在遍历(因为可能有多个文件)
这里一定要注意!if(!item.isFormField()) 这里的isFormField()表示是普通表单项;我们要对文件夹上传的表单项遍历,所以要用!item.isFormField()
5)得到上传文件名:item.getName();
6)创造路径:String path = “F:\img”; String path2 = file + “/”+fileName; File storeFile = new File(path2);
7)写入即可 item.write(storeFile);
8)把路径存放到数据库:String sql = “update user set fileName = ? where userId = ?”;
4.页面显示
注意,图片在服务器端,是无法通过绝对路径访问的,所以需要
<% String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort(); %>
得到上下文路径,然后:
<img src="<%=basePath%>/img/${user.fileName}">
这里要特别注意路径问题:我们没有用虚拟路径,是因为虚拟路径上传后,代码一旦变动,临时创建的文件夹就会自动消失,图片找不到!所以在写死路径之前,要这么操作下:
操作 同SpringMVC,略!
好了,这篇博客应该是早上发过的,结果刚才发上一篇博客的时候,发现不翼而飞了,难道和雪花一起融化了?