图片上传-下载-删除等图片管理的若干经验总结3-单一业务场景的完整解决方案

来源:互联网 发布:淘宝打印订单 编辑:程序博客网 时间:2024/06/04 18:07
这次完整地介绍图片上传的完整解决方案,如有bug,后续再补充。


一、图片表
CREATE TABLE `photo` (  `id` bigint(10) unsigned NOT NULL AUTO_INCREMENT,  `bizid` bigint(11) NOT NULL DEFAULT '-1' COMMENT '业务id,比如项目的id',  `cover` int(11) DEFAULT '0' COMMENT '1:是,0:不是',  `sort` int(11) DEFAULT '0' COMMENT '越小越靠前',  `url` varchar(200) DEFAULT NULL,  `name` varchar(255) DEFAULT NULL COMMENT '图片的原文件名',  `remark` varchar(255) DEFAULT NULL COMMENT '图片备注',  `status` int(11) DEFAULT '0' COMMENT '0:正常,1:已删除,2:临时的',  `type` int(11) DEFAULT '1' COMMENT '1:项目资料 2:待续',  `addtime` datetime DEFAULT NULL,  `uptime` datetime DEFAULT NULL,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=102 DEFAULT CHARSET=utf8 COMMENT='用户上传的图片';




关键字段
id:数据库主键
bizid:相关业务的id,比如某个项目project的主键id
type:相关业务的类型,比如type=1表名这个图片是某个项目的
status:这个图片的状态,0:正常状态,1:已删除(现在没有什么用,因为图片是物理删除的),2:临时的


图片物理删除,是考虑到磁盘空间容易不足。
不应该物理删除的理由是,今后可能会还原或者其它业务需要。


如果是逻辑删除,要注意“逻辑删除”和“临时上传的垃圾图片”物理删除是需要分开考虑的。
而如果是物理删除,则可以合并考虑。


二、图片上传后端代码
@RequestMapping("uploadImg")public void uploadImg(MultipartHttpServletRequest request,HttpServletResponse response, Long bizid,Integer type) {MultipartFile file = request.getFile("file");MultipartFileValidator validator = new MultipartFileValidator();validator.setAllowedContentTypes(new String[] { "image/jpeg","image/pjpeg", "image/png", "image/x-png" });String path = null;String name = null;try {if (null != file && !file.isEmpty()) {name = file.getOriginalFilename();validator.validate(file);path = storeFile(file);storeSlaveFile(file, path);} else {throw new Exception("file is empty");}} catch (Exception e) {e.printStackTrace();super.jsonFail(response, "图片格式不正确或大小超过1M");return;}Long id = null;if (bizid != null) {// 临时图片存起来Photo photo = new Photo();photo.setName(name);photo.setUrl(path);photo.setBizid(bizid);photo.setStatus(2);photo.setType(type);id = photoService.add(photo);}JSONObject jsonObject = new JSONObject();jsonObject.put("code", ErrorCode.OK.getCode());jsonObject.put("path", path);jsonObject.put("name", name);if (id != null) {jsonObject.put("id", id);}super.returnJsonObject(response, jsonObject);}


 1.存储图片
 2.存储photo信息
 3.返回新增图片的id、路径等信息给前端
 
 三、前端图片上传
 
function bindSaveEvent(){console.log("bindSaveEvent");$("#save").on("click", function() {var trs=$(".tr");var photos = new Array();$.each(trs,function(i,n){var tr = $(trs[i]);var photo ={};//newid是数据库中的idvar id=tr.attr("id");var newid=tr.attr("newid");photo.id=newid;photo.cover=$("#"+id+"-cover").val();photo.sort=$("#"+id+"-sort").val();photo.remark=$("#"+id+"-remark").val();photo.url=$("#"+id+"-img").attr("path");photo.name=$("#"+id+"-name").val();console.log("id="+id);photos[i]=photo;});var json=JSON.stringify(photos);console.log("id=${photoVo.project.id}"+",photos="+json);$.ajax( {        url:'project/savePhoto',    data:{        id : ${photoVo.project.id},    photos: json    },        type:'post',        cache:false,     async: true,    dataType:'json',        success:function(data) {    //alert(data.itemName);    //nameStr = data.itemName;    console.log("data="+data);    },         error : function() {             alert("保存图片失败!");       }    });  });}


1.采用异步上传,把一条图片信息的id、name等信息,传给后端保存。
2.一次性传入多个图
 存在“增加”和“删除”按钮。
 
function bindAddEvent(){console.log("bindAddEvent");$("#add").on("click", function() {var strTemplate='<tr id="{photo.id}" newid="" class="tr">'+'<td><input id="{photo.id}-name" type="text" value="" /></td>'+'<td><select id="{photo.id}-sort" class="sort">'+'<option value="1">1</option>'+'<option value="2">2</option>'+'<option value="3">3</option>'+'<option value="4">4</option>'+'<option value="5">5</option>'+'<option value="6">6</option>'+'<option value="7">7</option>'+'<option value="8">8</option>'+'<option value="9">9</option>'+'<option value="10">10</option>'+'<option value="0">0</option>'+'</select></td>'+'<td><select id="{photo.id}-cover" owner="{photo.id}"'+'class="cover">'+'<option value="0">否</option>'+'<option value="1">是</option>'+'</select></td>'+'<td><input type="text" id="{photo.id}-remark" value="" /></td>'+'<td><input type="file" id="{photo.id}-file" name="file"'+'style="width: 264px" onchange="uploadImg({photo.id});"><img'+' id="{photo.id}-img" src="" path=""'+' width="80%"></td>'+'<td><a id="{photo.id}-del" owner="{photo.id}"'+'href="javascript:;" class="del">删除</a></td>'+'</tr>';var photoId = new Date().getTime();var html = strTemplate.replace(/{photo.id}/g,photoId);var tbody = $("#tbody");tbody.append(html);console.log(html);//必须为新生成的对象,重新绑定事件bindDelEvent();bindCoverChangeEvent();});}


点击增加按钮,就多生成1个上传图片的控件。
点击删除按钮,就根据id删除某个图片上传空间。

四、图片保存
 
public void savePhoto(HttpServletResponse response, @RequestParam Long id,String photos) {List<Photo> list=JSONArray.parseArray(photos, Photo.class);projectService.savePhoto(id,list);super.jsonSucceed(response);}


id是所属项目的id,photos是前端所有图片的信息(json格式)

保存过程:
public void savePhoto(Long id, List<Photo> list) {if (CollectionUtils.isEmpty(list)) {logger.error("The photo list is empty~");return;}// 这个项目数据库中的图片,包括所有的状态List<Photo> dbList = photoDao.getPhotoListByProjectIdAllStatus(id);PhotoBean photoBean = handlePhoto(list, dbList);//理论上,不再存在add doAdd(id, photoBean.toAdd);doUpdate(id, photoBean.toUpdate);doDelete(id, photoBean.toDelete);}doUpdate和doDelete批量更新和批量删除方法,很清晰,不再赘述。//PhotoBean的结构class PhotoBean {//将要删除的,通常是数据库中的public List<Photo> toDelete;//将要更新的,都在数据库中,部分最新内容来源于web前端public List<Photo> toUpdate;//将要增加的,由于上传图片的时候都已经插入了,这个时候可以忽略了//public List<Photo> toAdd;} //根据前端photo集合和数据库photo集合,得到需要更新和需要删除的photo集合,不存在需要增加的photo集合private PhotoBean handlePhoto(List<Photo> list, List<Photo> dbList) {PhotoBean bean = new PhotoBean();// 全部删除,什么图片都没有上传if (CollectionUtils.isEmpty(list)) {bean.toDelete = dbList;}// 全部增加,一般在第1次/*if (CollectionUtils.isEmpty(dbList)) {bean.toAdd = list;}*/// 都不为nullif (list != null && dbList != null) {// 交集,肯定不为null,最多是empty,id相同就是共同存在List<Photo> commonList = ListUtils.retainAll(list, dbList);// 2者交集List<Photo> toUpdate = commonList;            //数据库中的临时图片,状态需要改为“正常”if(CollectionUtils.isNotEmpty(toUpdate)){for(Photo p:toUpdate){p.setStatus(0);}}// 在list,不再dbList中的/*List<Photo> toAdd = new ArrayList<Photo>();for (Photo p : list) {if (!dbList.contains(p)) {toAdd.add(p);}}*/// 在dbList,不在list中的List<Photo> toDelete = new ArrayList<Photo>();for (Photo p : dbList) {if (!list.contains(p)) {toDelete.add(p);}}//bean.toAdd=toAdd;bean.toUpdate=toUpdate;bean.toDelete=toDelete;}return bean;}



重写Photo的equals方法,id相等则相等。

五、写在最后
    1.由于个人喜欢在本地保存完整的文章,不喜欢图片,另外CSDN的相册也不怎么好用。
      我写的文章很少出现图片,大家凑合着看。
    2.由于图片管理是完整项目的一部分,不方便上传完整代码。
 先记下来,最近抽空,单独开一个工程,演示图片上传。
3.多图分开上传,是因为项目中的图片可以有 备注remark、排序sort等很多字段。
如果只需要url等少量字段的话,可以采用百度的WebUploader多图上传组件。
4.代码中,存在前端jQuery、后端Java代码。
5.图片的物理存储,可以存到本地,也可以用Fastdfs。
6.过几天单独建立多图演示项目的时候,我打算简化点,存储图片不用Fastdfs。
再单独搞个项目,演示Fastdfs的用法。
7.有需要的人士,自己参考整合多图上传到Fastdfs。
8.多图上传,真不是一个简单的问题,至少花了3个完整的工作日。

1 1
原创粉丝点击