文件上传三剑客(二)-后台数据库存储

来源:互联网 发布:java基础知识体系结构 编辑:程序博客网 时间:2024/06/05 09:18

承上启下

上一篇,说的是文件存储,这样的存储可以只存储保存文件的路径,简单好用,但是也有问题:

1.原来的文件名不能保存。由于文件存储,如果保存文件时候,保留文件名就会存在覆盖问题,所以要生成一个不重复的文件名。

2.不能灵活的控制上传文件的个数。

这一篇,咱们就来实现文件存储数据库的实现方式。js的原理是类似的,对每一个上传组件生成一个隐藏的form和iframe,然后提交。大家要耐心看一下。

数据库存储的效果

上传前:

上传成功后:

*这里的样式都是简单设置的,莫见怪,你可以自己按需求调整一下。

*可以看到这里不仅能保存文件原名,还可以重复上传,添加描述

数据库设计

为了打到重复使用和容易清理的目的,这里设计了通用的数据库结构(基于mysql的)

CREATE TABLE mm_attachment    (        id bigint NOT NULL AUTO_INCREMENT COMMENT '编号',        form VARCHAR(20) NOT NULL COMMENT '表单table的名称(为达到附件共用的目的)',        fid VARCHAR(30) NOT NULL COMMENT '表单记录的Id',        field VARCHAR(30) NOT NULL COMMENT '对应的字段',        fileContent longblob NOT NULL COMMENT '附件路径',        fileName VARCHAR(50) NOT NULL COMMENT '文件的名称',        fileSize bigint COMMENT '文件的大小',        fileDesc VARCHAR(60) COMMENT '文件的描述',        contentType VARCHAR(100) COMMENT '文件的mime类型',        createTime DATETIME,        createUser VARCHAR(30),        PRIMARY KEY (id)    )    ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='文件类附件表'

*在数据库中如何来找到这个附件所属的记录?我这里使用这三个“坐标”来定位:form(那张表)、fid(那条记录)、field(字段标识)。有了这三个字段就可以打到公用的目的。

*关于文件清理,可以做一个定时任务,查询每天增加的记录,核对它所属的域,如果域不存在就删除掉。

html代码

<table><tr><th>样例数据</th><td class="file"><input type="file" name="upfile"  />
<!--注意这里--><input name="upload" type="button" value="上传" fileNum="3" save="mvc/attachment/save" formName="mm_model" fid="${id==null?obj.id:id }" field="evaluateFile"/><input type="text" name="fileDesc" placeholder="文件描述:选填"/>
<!-- 这里往下的代码是"更新域"时候才用到的,用来回显已经上传的文件 --><c:if test="${not empty fileList }"><table class="fileTable"><thead><tr><th>序号</th><th>编号</th><th>文件名</th><th>大小(字节)</th><th>文件描述</th><th>操作</th></tr></thead><tbody><c:forEach items="${fileList }" var="file" varStatus="vs"><tr><td>${vs.index+1 }</td><td>${file.id }</td><td><a href="mvc/attachment/download?id=${file.id }">${file.fileName }</a></td><td>${file.fileSize }</td><td>${file.fileDesc }</td><td><a onclick="delFile('${file.id}');">删除</a></td></tr></c:forEach></tbody></table></c:if></td></tr></table>

*注意这里的上传按钮,有几个自定义属性,意思一看就明白了;

*<c:forEach>是JSTL标签,用在再次编辑时候的回显,如果只是测试上传的效果不需要。

js代码

$(function(){
/** * 数据库上传文件组件 *///如果有上传组件,就在body中添加隐藏层if($('input[save]').length>0){$('body').append('<div id="ddiv" style="display:none;"></div>');}//$('input[save]').click(function(){//anchorvar btn=$(this);var fileNum=btn.attr('fileNum');//限制上传文件的个数var save=btn.attr('save');//var file=$(this).siblings('input[type=file]');var formName=btn.attr('formName');var fid=btn.attr('fid');var field=btn.attr('field');//var fileDesc=btn.siblings('input[name="fileDesc"]');//判断已经上传的个数var hasNum=btn.siblings('table').children('tbody').children('tr').length;if(hasNum>=fileNum){alert("当前字段只允许"+fileNum+"个上传文件");return false;}//文件非空判断if(!file.val()){alert("请选择要上传的文件");return ;}//ddiv中添加iframeif($('#t'+field).length<=0){var hForm='<form method="post" enctype="multipart/form-data" action="'+btn.attr('save')+'" id="f'+field+'" target="t'+field+'">'+'<input type="hidden" name="formName" value="'+formName+'" />'+'<input type="hidden" name="fid" value="'+fid+'" />'+'<input type="hidden" name="field" value="'+field+'" />'+'</form>';var hFrame='<iframe name="t'+field+'" id="t'+field+'"></iframe>';$('#ddiv').append(hForm);$('#ddiv').append(hFrame);//提交成功后处理$('#ddiv #t'+field).on('load',function(){//file.insertBefore(btn);fileDesc.insertAfter(btn);$('#coverDiv').hide();var result=this.contentWindow.result;if(!result){alert("上传后台错误,请刷新页面重试,如依旧报错,请联系管理员!");return ;}if(result.result=='success'){//清空原来的上传文件组件file.val('');//$('#'+field).val(result.file.id);//if(btn.siblings('table').length<=0){btn.parent().append('<table class="fileTable"><thead><tr><th>序号</th><th>编号</th><th>文件名</th><th>大小(字节)</th><th>文件描述</th><th>操作</th></tr></thead><tbody></tbody></table>');}//表格中展示上传的文件var tab=btn.siblings('table');var recode='<tr><td>'+(tab.children('tbody').children('tr').length+1)+'</td>'+'<td>'+result.file.id+'</td>'+'<td>'+'<a href="mvc/attachment/download?id='+result.file.id+'">'+result.file.fileName+'</a>'+'</td>'+'<td>'+result.file.fileSize+'</td>'+'<td>'+result.file.fileDesc+'</td>'+'<td>'+'<a onclick="delFile(this,\''+result.file.id+'\')">删除</a>'+'</td></tr>';tab.children('tbody').append(recode);}else{alert("上传失败,"+result.message);}});}//file.appendTo($('#ddiv #f'+field));fileDesc.appendTo($('#ddiv #f'+field));//提交表单$('#coverDiv').show();$('#ddiv #f'+field).submit();});});/* * 删除附件 */function delFile(obj,id){var con=window.confirm("你确定要删除这条记录吗?");if(con){$.post('mvc/attachment/del',{id:id},function(data){alert(data.message);$(obj).parent().parent('tr').remove();},'json');}}

*js代码是基于jquery的实现;

*代码的原理和上一篇中的类似,用心看很简单

java服务器代码

/** * 存放数据库类型的文件上传 * @param request * @param upfile * @return */@RequestMapping(value="/save", method = RequestMethod.POST)public String save(HttpServletRequest request, @RequestParam("upfile") MultipartFile upfile){HttpSession session=request.getSession();UserInfo user=(UserInfo)session.getAttribute("user");//表单的参数String formName=request.getParameter("formName");String fid=request.getParameter("fid");String field=request.getParameter("field");String fileDesc=request.getParameter("fileDesc");//文件的参数String fileName=upfile.getOriginalFilename();long fileSize=upfile.getSize();String contentType=upfile.getContentType();HashMap obj=new HashMap();try {obj.put("form", formName);obj.put("fid", fid);obj.put("field", field);obj.put("fileContent", upfile.getInputStream());obj.put("fileName",fileName);obj.put("fileSize",fileSize);obj.put("fileDesc",fileDesc);obj.put("contentType",contentType);obj.put("createTime", new Date());obj.put("createUser", user.getLoginId());//保存到数据库service.addMap(obj);} catch (Exception e) {e.printStackTrace();//成功后返回内容request.setAttribute("result", "fail");request.setAttribute("message", "上传失败,后台报错");return "uploadResult2";}//成功后返回内容request.setAttribute("result", "success");request.setAttribute("message", "上传成功");Gson gson=new Gson();obj.remove("fileContent");request.setAttribute("file", gson.toJson(obj));return "uploadResult2";}/** * 存放数据库中的文件下载 * @param id * @param request * @param response */@RequestMapping("/download")public void download(@RequestParam("id")String id,HttpServletRequest request,HttpServletResponse response){Map obj=service.selectById(id);String fileName=(String)obj.get("fileName");byte[] file=(byte[])obj.get("fileContent");//mime类型String type = (String)obj.get("contentType");type=org.apache.commons.lang3.StringUtils.isBlank(type)?StringUtils.checkType(fileName):type;//可能的中文文件名处理try {fileName=URLEncoder.encode(fileName,"utf8");} catch (UnsupportedEncodingException e1) {e1.printStackTrace();}    response.addHeader("Content-Disposition", "attachment;filename=" + fileName);    response.addHeader("Content-Length ", String.valueOf(file.length));    response.setContentType(type);    OutputStream writer=null;try {writer = response.getOutputStream();writer.write(file);writer.flush();} catch (IOException e) {e.printStackTrace();}finally{try {if(writer!=null){writer.close();writer=null;}} catch (IOException e) {e.printStackTrace();}}}/** * 存放数据库文件的删除 * @param request * @param id */@RequestMapping("/del")@ResponseBodypublic Object delFile(HttpServletRequest request,@RequestParam("id") String id){HashMap<String,String> result=new HashMap<String,String>();try{int a=service.del(id);if(a!=1){//删除异常result.put("result", "fail");result.put("message", "删除异常,影响行数"+a);return result;}result.put("result", "success");result.put("message", "文件删除成功!");}catch(Exception e){//删除文件,后台报错result.put("result", "fail");result.put("message", "删除文件,后台报错!");}return result;}

*java代码是基于spring-mvc+spring+mybatis的;

*service调用就是特别基本的mybatis写法,这里不粘贴代码了,有不明白的可以问我要一份

返回页面代码

<%@ page language="java" contentType="text/html; charset=UTF-8"    pageEncoding="UTF-8"%><%String path = request.getContextPath();String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";%><!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"><title>Insert title here</title><base href="<%=basePath%>"><script type="text/javascript">result={"result": '${result}',"message": '${message}',"file": ${file }}</script></head><body></body></html>

*这里的返回页面都是最简单的实现,没有进度效果。

关于上传进度这个,我还没有具体来做过,一起fileupload组件的时候,记得有个上传监听器,当时考虑能够实现进度,并没有具体的做。

这里主要是讨论了文件上传的存储方案和优劣,使用的也是springmvc先就不考虑进度问题,大家可以讨论一下,后续有时间,我也会补充这部分内容


0 0
原创粉丝点击