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


    这次完整地介绍图片上传的完整解决方案,如有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是数据库中的id
    				var 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=" 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;
    		}*/
    		// 都不为null
    		if (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个完整的工作日。

  • 相关阅读:
    基本语句
    mysql多表查询方法(join)
    MySQL JOIN 多表连接
    MySQL SHOW INDEX语法的实际应用
    1.索引作用
    MySQL索引和优化查询
    mysql复合索引、普通索引总结
    mysql 索引相关
    for循环的break和continue
    保护程序猿滴眼睛---修改VS 2012 编辑器颜色
  • 原文地址:https://www.cnblogs.com/qitian1/p/6462654.html
Copyright © 2020-2023  润新知