基于SpringMVC的文件(增删改查)上传、下载、更新、删除
一、项目背景
摘要:最近一直在忙着项目的事,3个项目过去了,发现有一个共同的业务,那就是附件的处理,附件包括各种文档,当然还有图片等特殊文件,由于时间的关系,每次都是匆匆忙忙的搞定上线,称这项目的空档,整理了一份附件上传、下载、删除的项目,主要就是附件的处理,情况包含以下几种:
1. 表单个附件共存
2. 只有附件
3. 只有表单
其中,后两种处理方式简单,本文主要说明的是第一种的处理方案。
二、项目需求
整体来说,项目需求还是不复杂的,这里单独把附件和表单数据提交拿出来说,就是表单中的有附件的情况,表单中的附件随时可以进行替换、删除、添加等操作。折腾了很久,终于把附件上传这档子事理清楚了,这里做个记录,与各位大神共勉。
三、项目架构
项目架构采用的是比较常用的传统的javaWeb项目开发框架,Spring4.3.4,hibernate5(ssh),MySQL 5.7,Tomcat7.0,关于该项目的如何整合,就不再多说了,网上都有,搭建一套框架,应该不是问题。该业务实现的思想就是:数据库存放文件路径,这里是物理路径,注意物理路径和虚拟路径的区别,文件存放在服务器,需要的时候通过数据库表中的物理路径可以找到相应的文件,增删改查都是可以的。
四、技术实现
4.1 数据库创建
打开MySQL管理工具或者CMD dos界面进入MySQL创建数据库,这里,我使用管理工具创建的,首先是文件表:
字段可以根据业务的不同适当添加,我做个例子,有这几个字段就够了,其中relationID是和我们的业务表管理的,主外键关联或者普通关联。下面是业务表的创建:
数据库表大概就是这样,附件表和业务表关联,当然关联的方式有很多,我只选择了最简单的主外键关联。
4.2 后台代码编写
项目架构使用的hibernate,hibernate主要的有点就是基于对象,非常适合面向对象编程的本质,下面创建对象,采用Spring注解的方式:
package com.common.model; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@Table(name="tab_userinfo")
public class TabUserinfo { @Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String username;
private String password;
private String relationID;
private String remark; public TabUserinfo() {
super();
} public TabUserinfo(int id, String username, String password, String remark) {
super();
this.id = id;
this.username = username;
this.password = password;
this.remark = remark;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getRelationID() {
return relationID;
} public void setRelationID(String relationID) {
this.relationID = relationID;
} public String getRemark() {
return remark;
} public void setRemark(String remark) {
this.remark = remark;
} @Override
public String toString() {
return "TabUserinfo [id=" + id + ", username=" + username + ", password=" + password + ", relationID="
+ relationID + ", remark=" + remark + "]";
} }
package com.common.model; import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table; @Entity
@Table(name="tab_userinfo")
public class TabUserinfo { @Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String username;
private String password;
private String relationID;
private String remark; public TabUserinfo() {
super();
} public TabUserinfo(int id, String username, String password, String remark) {
super();
this.id = id;
this.username = username;
this.password = password;
this.remark = remark;
} public int getId() {
return id;
} public void setId(int id) {
this.id = id;
} public String getUsername() {
return username;
} public void setUsername(String username) {
this.username = username;
} public String getPassword() {
return password;
} public void setPassword(String password) {
this.password = password;
} public String getRelationID() {
return relationID;
} public void setRelationID(String relationID) {
this.relationID = relationID;
} public String getRemark() {
return remark;
} public void setRemark(String remark) {
this.remark = remark;
} @Override
public String toString() {
return "TabUserinfo [id=" + id + ", username=" + username + ", password=" + password + ", relationID="
+ relationID + ", remark=" + remark + "]";
} }
文件上传:
@CrossOrigin(origins = "*", maxAge = 3600)
@Controller
@RequestMapping("user")
public class UserController {
protected Logger log = Logger.getLogger(UserController.class);
@Autowired
ServiceI service; @RequestMapping(value="userAddFile",produces = {"application/json;charset=UTF-8"},method=RequestMethod.POST)
@ResponseBody
public String addFile(TabUserinfo userinfo,@RequestParam(value="file",required=true) MultipartFile [] uploadFile,
HttpServletRequest request){ JSONObject jsonRusult=new JSONObject();
String UUIDString=UUID.randomUUID().toString();
String date=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
boolean flag=false;
List<Object> fileList=null;
ReturnStatus rStatus=null;
log.info("UUID:"+UUIDString);
//保存表单数据
try {
if(null!=userinfo){
log.info("数据userinfo:"+userinfo.toString());
userinfo.setRelationID(UUIDString);
service.saveOrUpdate(userinfo);
flag=true;
}
if(flag){
//上传附件
fileList=FileUtils.uploadFile(service, UUIDString, date, uploadFile, request);
}
if(null!=fileList && fileList.size()!=0){
rStatus=new ReturnStatus("0000", "文件上传成功!");
jsonRusult.put("status", rStatus);
jsonRusult.put("fileInfo", fileList);
}else{
rStatus=new ReturnStatus("0003", "文件上传失败!");
jsonRusult.put("fileInfo", null);
} } catch (IllegalStateException e) {
log.error("文件上传失败",e);
service.delete(userinfo);//保证事务
} catch (IOException e) {
log.error("文件上传失败",e);
service.delete(userinfo);
} return JSON.toJSONString(jsonRusult,SerializerFeature.WriteMapNullValue);
}
文件上传工具类:
public static List<Object> uploadFile(ServiceI service,String relationID,String date, MultipartFile [] uploadFile,HttpServletRequest request)
throws IllegalStateException, IOException{ ServletContext servletContext = request.getServletContext();
List<Object> fileList=new ArrayList<>();
UploadFile fileEntity=null;
for(int i=0;i<uploadFile.length;i++){
String filePath= servletContext.getRealPath("/upload");
log.info("文件存放磁盘路径:"+filePath);
String rePath=request.getScheme()+"://"+request.getServerName()+":"+
request.getServerPort()+request.getContextPath()+"/upload";
log.info("取文件路径:"+rePath);
MultipartFile file=uploadFile[i];
String fileName=file.getOriginalFilename();
String fileNameS="";
String fileType=fileName.split("\\.")[fileName.split("\\.").length-1];
if(StringUtil.isNull(fileName,fileType)){
fileNameS=UUID.randomUUID()+"."+fileType;
if(!file.isEmpty()){
if(fileType.contains("jpg") || fileType.contains("png") || fileType.contains("gif")){
filePath+="\\image\\"+fileNameS;
rePath+="/image/"+fileNameS;
}else{
filePath+="\\file\\"+fileNameS;
rePath+="/file/"+fileNameS;
}
log.info("文件路径filePath:"+filePath);
log.info("文件路径rePath:"+rePath);
File fileS=new File(filePath);
if(!fileS.getParentFile().exists()){
fileS.getParentFile().mkdirs();
}
file.transferTo(fileS); fileEntity=saveFileInfo(service,fileName,filePath,relationID,date);
if(null!=fileEntity){
fileEntity.setFilePath(rePath);
fileList.add(fileEntity);
} }else{
fileList.add("文件不存在");;
}
}
}
return fileList;
}
/**
* 保存文件信息
* @param fileName
* @param filePath
*/
private static UploadFile saveFileInfo(ServiceI service,String fileName,String filePath,String relationID, String date) { UploadFile fileEntity=new UploadFile();
fileEntity.setFileName(fileName);
fileEntity.setFilePath(filePath);
fileEntity.setRelationID(relationID);
fileEntity.setUploadTime(date);
service.saveOrUpdate(fileEntity);
return fileEntity;
}
这里需要注意的是:在多文件上上传的时候一定要注意,文件路径的获取,一定是每个文件获取一次,如下图:
如果一次性获取,会发生意外:如图
文件路径找不到
这样只能上传第一个文件,而且业务表中没有成功插入数据。
4.3 Postman进行测试:
下面我们再来看数据库中是否有数据
证明我们的接口是好用的,这里解释下为什么要返回文件的相关信息,因为对于图片来说,我们会上传完成显示预览图,对于文件来说返回链接,可以下载查看等,因此这么返回的路径。在前段中配置SRC就可以进行下载操作
如下图:
我们把链接复制进浏览器首先看图的:
再来看文件的:
这样可以方便我们对文件进行后续的操作。
下面来说正事,我在这个项目上面才过的坑,希望大家引以为戒,不要掉进去。
1. 文件上传路径
因为我们的项目是在Eclipse上进行开发测试的,因此上传的文件会存在Eclipse工作空间中去,存到工作空间之后,在E:\workspace\eclipse_workspace\.metadata\.plugins\org.eclipse.wst.server.core\tmp0\webapps下,是没有权限访问的,因此读取文件的时候会报错,所以最好的办法就是制定一个本地服务器的物理路径,然后通过Tomcat中的server.xml 进行配置映射,存到服务其中的物理路径是:E:\service\webapps\common\upload\file\fd6e9dfb-7d1c-4fc6-8954-851258f6acc4.doc,那么只要我们开启的Tomcat服务,就可以通过:IP/common/upload/file/fd6e9dfb-7d1c-4fc6-8954-851258f6acc4.doc访问到我们上传的文件。其中common是项目名称,upload是指定上传的文件夹。
另一种情况,我们可以直接把项目打成war包部署在Tomcat服务上,上传的文件就可以在我们服务器部署的位置找到,我采取的就是这种方式:
当然了,我们也可以通过接口的方式对文件进项下载,思想就是:通过相关条件找到数据库中存放的文件路径,拿到文件路径生成文件以二进制的方式返回给浏览器。下面是采用SpringMVC开发的下载文件接口:
/**
* 返回下载流的二进制
* @param path
* @return
* @throws UnsupportedEncodingException
*/
public static ResponseEntity<byte[]> getStreamByPath(String filePath,HttpServletRequest request,
HttpServletResponse response,String fileName) throws UnsupportedEncodingException { response.setCharacterEncoding("utf-8");
response.setContentType( "application/x-msdownload");
response.addHeader("Content-Disposition","attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));// 设置文件名
System.out.println("fdsfdsfsdf:"+filePath);
URL url=null;
ResponseEntity<byte[]> entity=null;
InputStream is =null;
try {
// url=new URL(filePath); // System.out.println(url.toString());
// File file=new File(url.getPath());
File file=new File(filePath);
if(file.exists()){
String mimeType = URLConnection.guessContentTypeFromName(fileName);
if(mimeType==null){
mimeType = "application/octet-stream";
}
response.setContentType(mimeType);
byte[] body = null;
is = new FileInputStream(file);
body = new byte[is.available()];
is.read(body);
HttpHeaders headers = new HttpHeaders();
headers.add("Content-Disposition", "attchement;filename="+ URLEncoder.encode(fileName, "UTF-8"));
HttpStatus statusCode = HttpStatus.OK;
headers.setContentDispositionFormData("attachment", file.getName());
headers.setContentType(MediaType.APPLICATION_OCTET_STREAM);
entity = new ResponseEntity<byte[]>(body, headers, statusCode);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}finally{
if(is!=null){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
} return entity;
}
下面是文件删除,文件删除其实很简单了,我们按照相关条件找到文件,然后删除即可。
/**
* 文件删除
* @param path
* @return
* @throws UnsupportedEncodingException
*/
public static boolean deleteFile(String filePath,HttpServletRequest request,
HttpServletResponse response,String fileName) throws UnsupportedEncodingException {
boolean flag=false;
if(StringUtil.isNull(fileName,filePath)){
File file=new File(filePath);
if(file.exists()){
flag=file.delete();
}
} return flag; }
切记:删除文件和数据库中的记录一定是一个事务,删除记录的同时删除数据库中的记录,否则会出现数据不一致的情况。
最后附上相关配置文件:
1. SpringMVC.xml
2. web.xml
五、总结
在做这些项目的时候,遇到的文件上传的坑大概就这么多,目前想到的附件上传只有这一种方式,大家如果有什么好的方法,欢迎评论区讨论!
源码下载: https://download.csdn.net/download/qq_42389242/10746764
基于SpringMVC的文件(增删改查)上传、下载、更新、删除的更多相关文章
- 基于springmvc的简单增删改查实现---中间使用到了bean validation
package com.kite.controller; import java.util.HashMap; import java.util.Map; import javax.validation ...
- 【基础篇】js对本地文件增删改查--查
前置条件: 1. 本地有安装node,点击传送门 项目目录: 1. msg.json内容 { "data": [ { "id": 1, "name&q ...
- 【基础篇】js对本地文件增删改查--改
前置条件: 1. 本地有安装node,点击传送门 项目目录: 1. msg.json内容 { "data": [ { "id": 1, "name&q ...
- 【基础篇】js对本地文件增删改查--删
前置条件: 1. 本地有安装node,点击传送门 项目目录: 1. msg.json内容 { "data": [ { "id": 1, "name&q ...
- 【基础篇】js对本地文件增删改查--增
前置条件: 1. 本地有安装node,点击传送门 项目目录: 1. msg.json内容 { "data": [ { "id": 1, "name&q ...
- 基于pymysql模块的增删改查
上课笔记 重点:(熟练)多表查询创建存储过程原生sql索引原理 pymysql 封装好的客户端cursor 底层就是一个send操作commit 告诉mysql真的要完成修改操作(不然修改不会生效)e ...
- 【基础篇】js对本地文件增删改查
[基础篇] js对本地文件增删改查--增 js对本地文件增删改查--删 js对本地文件增删改查--改 js对本地文件增删改查--查
- 仿联想商城laravel实战---5、无刷新的增删改查(动态页面更新的三种方式(html))
仿联想商城laravel实战---5.无刷新的增删改查(动态页面更新的三种方式(html)) 一.总结 一句话总结: 直接js增加删除修改html 控制器直接返回处理好的页面 用双向绑定插件比如vue ...
- Java 客户端操作 FastDFS 实现文件上传下载替换删除
FastDFS 的作者余庆先生已经为我们开发好了 Java 对应的 SDK.这里需要解释一下:作者余庆并没有及时更新最新的 Java SDK 至 Maven 中央仓库,目前中央仓库最新版仍旧是 1.2 ...
- Python Web实战:Python+Django+MySQL实现基于Web版的增删改查
前言 本篇使用Python Web框架Django连接和操作MySQL数据库学生信息管理系统(SMS),主要包含对学生信息增删改查功能,旨在快速入门Python Web,少走弯路.效果演示在项目实战最 ...
随机推荐
- 限流——spring-cloud-zuul-ratelimit
先留个坑,慢慢补 git代码Demo:https://github.com/islowcity/spring-cloud-zuul-ratelimiter.git 有时间再写分析
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(63)-WebApi与Unity注入
系列目录 前言: 有时候我们系统需要开放数据给手机App端或其他移动设备,不得不说Asp.net WebApi是目前首选 本节记录Asp.net MVC WebApi怎么利用Unity注入.系列开头已 ...
- PHP从入门到精通(二)
PHP从入门到精通 之PHP中的函数 各位开发者朋友大家好,自上次更新PHP的相关知识,得到了大家的广泛支持.PHP的火爆程度不言而喻,函数作为PHP中极为重要的部分,应诸位的支持,博主继续跟进更新 ...
- Visual studio2015 编译时提示“GenerateResource”任务意外失败。
今天弄了一个winfrom程序,狗血,一直报错,在另一台电脑上就不报错. 错误如下图 其实这样也能运行,但就是代码改之后,没有办法调试.搜了很久,发现了一种解决办法,完美解决. 最终成功了.
- Python-集合-17
''' 集合:可变的数据类型,他里面的元素必须是不可变的数据类型,无序,不重复. {} ''' set1 = set({1,2,3}) # set2 = {1,2,3,[2,3],{'name':'a ...
- rabbitMq实现与zookeeper类似的watcher功能
场景:A.B.C.D(可以是一个机器的不同进程,也可以是不同机器的进程)启动了相同的项目,使用同一个数据库.但是,如果A修改了数据库的数据,需要B.C.D在很短的时间能够知道数据库发生了修改.当然可以 ...
- throws和throw抛出异常的使用规则
一直对java中的throws和throw不太理解.最近一直在查这两个方面的资料,算是能明白一点吧.如果我下面的观点哪有不对,希望指出来,我加以改进. throw:(针对对象的做法) ...
- C 实现选择排序
一.选择排序的思想 假设有一个7元素的数组 [11, 24, 5, 17, 2, 8, 20],我们通过选择排序来从小到大排序. 思想是进行7次外循环从0-->6,每一次又是一个内循环,从i+1 ...
- 用IntelliJ IDEA编译,编译之后提示 无效的标记: -release
软件版本:ideaIU-2016.3.2 JDK:jdk-9.0.4_windows-x64_bin 开始的时候建立一个maven项目,发现编译的时候提示[无效的标记: -release],以为是项目 ...
- Activiti动态设置办理人扩展
关键词:Assignee.Candidate users.Candidate groups:setAssignee.taskCandidateUser.taskCandidateGroup 主要解决问 ...