一、项目背景 

  摘要:最近一直在忙着项目的事,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

最新文章

  1. MSSQLServer 纵向表转横向表 横向表转纵向表 行转列 列转行
  2. angularjs 下拉框
  3. android 动态设置控件宽高度
  4. ready与onload区别一
  5. Kafka是分布式发布-订阅消息系统
  6. Lua中的require
  7. c# 时间戳转换为Datetime类型的时间
  8. html-php深入理解
  9. java小程序整理及排序算法
  10. TI CC254x BLE教程 3
  11. TextView 实现复制文本功能
  12. POJ 3191 The Moronic Cowmpouter(进制转换)
  13. 条形图(diagrams)
  14. Unity随手记
  15. SharePoint 2007 制作值班表
  16. sql查询单表之中大于2条的数据
  17. python3 操作配置文件
  18. 在windows下如何快速搭建web.py开发框架
  19. Expression 生成 Lambda
  20. 高分辨率下放大netbeans中的小图标

热门文章

  1. Item 9: 比起typedef更偏爱别名声明(alias declaration)
  2. 关于树莓派HDMI转VGA线接显示器黑屏
  3. host大法之GitHub上不去
  4. Linux下对文件进行加密备份的操作记录
  5. Python-文件操作—_19
  6. HAOI2016 找相同字符 后缀自动机
  7. Echarts学习求教
  8. JavaScript —— 数组
  9. 理解java的三大特性之继承
  10. opencv学习笔记(五)