应项目要求,使用MongoDB的GridFS来存储文件数据。
需求点:1、要求可实现多文件上传;2、可以动态新增、删除文件列表中的文件;3、使用文件ID下载;4、支持跨域;
提示:SpringMVC在4.2版本中有快捷的跨域处理方式@CrossOrigin
第一步:使用LayUI的upload控件编写JSP页面。
<div class="layui-form-item">
<label class="layui-form-label">附件说明</label>
<div class="layui-upload">
<button type="button" class="layui-btn layui-btn-normal" id="selectAttachment">选择多文件</button>
<button type="button" class="layui-btn" id="uploadAttachments">开始上传</button>
<div class="layui-upload-list">
<table class="layui-table">
<thead>
<tr><th>文件名</th><th>大小</th><th>状态</th><th>操作</th></tr>
</thead>
<tbody id="attachmentList"></tbody>
</table>
</div>
<input type="hidden" name="attachDownloadUrls" id="attachDownloadUrls"/>
</div>
</div>
第二步:编写JS代码
<script>
var file_server_url = "http://localhost:8099";
layui.use(['form', 'upload','layer'], function(){
var form = layui.form
,layer = layui.layer
,upload = layui.upload;
var attachDownloadUrls = [];//文件下载路径数组
var fileCount = 0;//当前文件的数量
//多文件列表示例
var demoListView = $('#attachmentList'),uploadListIns = upload.render({
elem: '#selectAttachment'
,url: file_server_url+'/attachment/upload'
,accept: 'file'
,multiple: true
,auto: false
,data:{'applyCode':$("#applyCode").val()}//额外的参数
,bindAction: '#uploadAttachments'
,choose: function(obj){
var files = this.files = obj.pushFile(); //将每次选择的文件追加到文件队列
//读取本地文件
obj.preview(function(index, file, result){
var tr = $(['<tr id="upload-'+ index +'">'
,'<td>'+ file.name +'</td>'
,'<td>'+ (file.size/1014).toFixed(1) +'kb</td>'
,'<td>等待上传</td>'
,'<td>'
,'<button class="layui-btn layui-btn-xs attach-reload layui-hide">重传</button>'
,'<button class="layui-btn layui-btn-xs layui-btn-danger attach-remove">移除</button>'
,'</td>'
,'</tr>'].join(''));
//单个重传
tr.find('.attach-reload').on('click', function(){
obj.upload(index, file);
});
//删除
tr.find('.attach-remove').on('click', function(){
delete files[index]; //删除对应的文件
tr.remove();
uploadListIns.config.elem.next()[0].value = ''; //清空 input file 值,以免删除后出现同名文件不可选
});
demoListView.append(tr);
});
}
,done: function(res, index, upload){
if(res.code == 0){ //上传成功
attachDownloadUrls.push(res.data.src);
fileCount++;
var tr = demoListView.find('tr#upload-'+ index),tds = tr.children();
tds.eq(2).html('<span style="color: #5FB878;">上传成功</span>');
tds.eq(3).html('<button type="button" data-id='+res.data.fileId+'_'+fileCount+' class="layui-btn layui-btn-xs layui-btn-danger attach-delete">删除</button>'); //清空操作
//删除
tr.find('.attach-delete').on('click', function(){
var dataSetId = this.dataset.id;
var dataArray = dataSetId.split('_');
$.ajax({
type : "POST",
url : file_server_url+"/attachment/delete",
data : "fileId="+dataArray[0],
success : function(result){
if(result.code==0){
setTimeout(function(){
layer.msg("文件删除成功!");
attachDownloadUrls.splice(fileCount-1,1);//删除文件URL
tr.remove();
},2000);
}else{
alert(result.msg);
}
}
});
});
return delete this.files[index]; //删除文件队列已经上传成功的文件
}
this.error(index, upload);
}
,error: function(index, upload){
var tr = demoListView.find('tr#upload-'+ index)
,tds = tr.children();
tds.eq(2).html('<span style="color: #FF5722;">上传失败</span>');
tds.eq(3).find('.attach-reload').removeClass('layui-hide'); //显示重传
}
});
//......其他操作
});
</script>
第三步:使用SpringMVC+MongDB+GridFS处理文件上传、下载、删除操作
1)applicationContext-dao.xml文件配置。(此文件还可以使用Properties文件进行配置,亦可以再丰富mongo的相关属性)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mongo="http://www.springframework.org/schema/data/mongo"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd
http://www.springframework.org/schema/data/mongo http://www.springframework.org/schema/data/mongo/spring-mongo-1.8.xsd">
<!-- 数据库连接池 -->
<!-- 加载配置文件 -->
<context:property-placeholder location="classpath:resource/*.properties" />
<mongo:db-factory id="mongoDbFactory" dbname="health_file" host="127.0.0.1" port="27017" />
<mongo:mapping-converter id="mongoConveter" db-factory-ref="mongoDbFactory"/>
<mongo:gridFsTemplate id="gridFsTemplate" converter-ref="mongoConveter" db-factory-ref="mongoDbFactory"/>
</beans>
2)application-service.xml的配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<context:component-scan base-package="com.hy.health.file.service" />
</beans>
3)springmv.xml文件的配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="com.hy.health.file.web"/>
<mvc:annotation-driven />
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
<!-- 定义文件上传解析器 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 设定默认编码 -->
<property name="defaultEncoding" value="UTF-8"></property>
<!-- 设定文件上传的最大值5MB,5*1024*1024 -->
<property name="maxUploadSize" value="5242880"></property>
</bean>
</beans>
4)编写业务层接口,主要包括以下几个方法。
public interface IFileService {
ResponseResult storeUploadFile(MultipartFile file, String applyCode);
ResponseEntity<InputStreamResource> attachmentDownload(String fileId);
ResponseResult attachmentList(String applyCode);
ResponseResult attachmentDelete(String fileId);
}
5)编写业务层实现类。
@Service
public class FileService implements IFileService {
@Autowired
GridFsTemplate gridFsTemplate;
@Value("${FILE_SERVER_BASEURL}")
private String FILE_SERVER_BASEURL;
@Override
public ResponseResult storeUploadFile(MultipartFile file, String applyCode) {
DBObject metadata = new BasicDBObject();
metadata.put("applyCode", applyCode);
metadata.put("originalFilename", file.getOriginalFilename());//保存原始的文件名
metadata.put("contentType", file.getContentType());
String suffix = file.getOriginalFilename().substring(file.getOriginalFilename().lastIndexOf("."));
String fileName = IDUtils.genItemId() + suffix;//处理存储的文件名字
GridFSFile gridFSFile = null;
try {
gridFSFile = gridFsTemplate.store(file.getInputStream(), fileName, metadata);
} catch (IOException e) {
e.printStackTrace();
return ResponseResult.FAIL();
}
String fileId = gridFSFile.getId().toString();
String resultUrl = FILE_SERVER_BASEURL+"/attachment/download/" + fileId;
Map<String, String> data = new HashMap<>();
data.put("src", resultUrl);
data.put("fileId", fileId);
return ResponseResult.OK(data);
}
@Override
public ResponseEntity<InputStreamResource> attachmentDownload(String fileId) {
Query query = new Query(Criteria.where("_id").is(fileId));
GridFSDBFile gridFsDbFile = gridFsTemplate.findOne(query);
String fileName = gridFsDbFile.getMetaData().get("originalFilename").toString();
try {
fileName = new String(fileName.getBytes("UTF-8"),"iso-8859-1");//文件名转码
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
HttpHeaders header = new HttpHeaders();
header.setContentType(MediaType.valueOf(gridFsDbFile.getMetaData().get("contentType").toString()));
header.setContentDispositionFormData("attachment", fileName);
InputStreamResource resource = new InputStreamResource(gridFsDbFile.getInputStream());
return new ResponseEntity<>(resource,header,HttpStatus.CREATED);
}
@Override
public ResponseResult attachmentList(String applyCode) {
Query query = new Query(new Criteria().is("{'metadata':{'applyCode':'" + applyCode + "'}})"));//通过metadata中的applyCode获取当前申请单中所有的文件列表。
List<GridFSDBFile> fsFileList = gridFsTemplate.find(query);
List<String> fileNameList = new ArrayList<String>();
for(GridFSDBFile file : fsFileList) {
fileNameList.add(file.getMetaData().get("originalFilename").toString());
}
return ResponseResult.OK(fileNameList);
}
@Override
public ResponseResult attachmentDelete(String fileId) {
Query query = new Query(Criteria.where("_id").is(fileId));
gridFsTemplate.delete(query);
return ResponseResult.SUCCESS();
}
}
注意:此实现类中使用了SpringMVC中ResponseEntity的方式进行文件的下载。
6)Controller层。
@RestController
public class AttachController {
@Autowired
IFileService fileService;
@CrossOrigin("http://localhost:8081")
@RequestMapping(value="/attachment/upload",method=RequestMethod.POST)
@ResponseBody
public ResponseResult attachmentUpload(@RequestParam(value = "file") MultipartFile file,String applyCode) {
return fileService.storeUploadFile(file, applyCode);
}
@CrossOrigin("http://localhost:8081")
@RequestMapping(value="/attachment/download/{fileId}",method=RequestMethod.GET)
public ResponseEntity<InputStreamResource> attachmentDownload(@PathVariable String fileId){
return fileService.attachmentDownload(fileId);
}
@CrossOrigin("http://localhost:8081")
@RequestMapping(value="/attachment/list",method=RequestMethod.POST)
@ResponseBody
public ResponseResult attachmentList(String applyCode) {
return fileService.attachmentList(applyCode);
}
@CrossOrigin("http://localhost:8081")
@RequestMapping("/attachment/delete")
@ResponseBody
public ResponseResult attachmentDelete(String fileId) {
return fileService.attachmentDelete(fileId);
}
}
注意:
1)LayUI的多文件上传其实为多文件逐个文件上传,也就是将文件数组中的文件循环调用文件上传方法。
2)此Service层中的文件上传的方法为单文件的方式,若为原始或其他前端实现可稍微改造即可。