zoukankan      html  css  js  c++  java
  • 详细阐述Web开发中的图片上传问题

        Web开发中,图片上传是一种极其常见的功能。但是呢,每次做上传,都花费了不少时间。

        一个“小功能”花费我这么多时间,真心不愉快。

       So,要得认真分析下原因。

    1.在最初学习Java Web开发的时候,经验不足,属于能力问题,比如对技术认识不到位。

    2.图片上传是一类问题,而不是一个问题。
       比如,大家都会做饭,但每个人自己做饭是有不同的。做了一个人吃、一家人吃、喜事待客做好几桌,是不同的问题。
       同样的,图片上传,是上传一张还是多张,前端的用户体验如何,后端逻辑处理是否正确,图片存储是否可靠。
       本文主要探讨这个问题。

    3. 有些情况,真不是我的问题。
       比如,前端上传组件是别人写的,一会这样做,一会那样做,后端不断改。
       比如,代码本来都不是我写的,是因为需求变化了,我接手了,然后自己改。 

    -------------------------------------------------------------------------
       下面,重点探讨第2个原因,技术问题。

    a.上传单个图片
      html写个file类型的input,form设置一下,enctype="multipart/form-data"。
      
      后端写个方法处理下就好,简要2点,1是用
    @RequestParam MultipartFile 接收一个文件,2是用FileUtils复制临时文件到目标文件
      
       
     @RequestMapping(value = "oneFileUpload", method = RequestMethod.POST)
        public String uploadLoginSplash(HttpServletRequest request, @RequestParam("file") MultipartFile file)
                throws IOException {
            if (!file.isEmpty()) {
                String realPath = request.getSession().getServletContext().getRealPath(SPLASH);
                // 这里不必处理IO流关闭的问题,因为FileUtils.copyInputStreamToFile()方法内部会自动把用到的IO流关掉,我是看它的源码才知道的
                FileUtils.copyInputStreamToFile(file.getInputStream(), new File(realPath, SPLASH_JPG));
            }
    
            return "manager/setting/settingManager";
        }



    b. 上传多个图片。
     方法一:
      定义多个file,后端用数组接收@RequestParam("file") CommonsMultipartFile[] files。
     一种比较灵活的方式是,写JS方法,点击“添加”和“删除”,可以选择增加上传图片的“上传框”。

     这种方式,我没有去实践,在CSDN上看到了一篇不错的帖子,属于看了但没有去实践。

    方法二:
    使用图片上传组件,相比方法一,更加灵活,但也有缺点,后端接收不能直接使用数组CommonsMultipartFile[] files,我尝试了不太行。
    前端上传组件,尝试的有webuploader,可以一次性选择多张图片,但是分批上传的。
    尼玛,百度了下,原来是百度团队搞的,怪不得看起来用起来,好高端大气的样子。
    官网:http://fex-team.github.io/webuploader/

    最先用的是,dropzone,感觉还行,但是没有百度的看起来美观,一次性可以选择多张图片,但一次性全部上传。
    官网:http://www.dropzonejs.com/
    最先的最先,某个同事用的是jquery的上传组件,印象中是的。

     后端处理多张图片的代码,比较通用的。
    @RequestMapping("/idCardImageUpload")
    	public void idCardImageUpload(HttpServletRequest request,
    			HttpServletResponse response) throws IllegalStateException,
    			IOException {
    		// 创建一个通用的多部分解析器
    		CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
    				request.getSession().getServletContext());
    		String finalFileName="";
    		// 判断 request 是否有文件上传,即多部分请求
    		if (multipartResolver.isMultipart(request)) {
    			// 转换成多部分request
    			MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
    			// 取得request中的所有文件名
    			Iterator<String> iter = multiRequest.getFileNames();
    			while (iter.hasNext()) {
    				// 取得上传文件
    				MultipartFile file = multiRequest.getFile(iter.next());
    				if (file != null) {
    					// 取得当前上传文件的文件名称
    					String fileName = file.getOriginalFilename();
    					// 如果名称不为“”,说明该文件存在,否则说明该文件不存在
    					if (StringUtils.isNotEmpty(fileName)) {
    						String[] strs = fileName.split("\.");
    						String fileExtention="png";
    						// 重命名上传后的文件名
    						if(strs.length >=2){
    							fileExtention=strs[strs.length-1];
    						}
    						try {
    							fileName = AES.Encrypt(fileName, DateFormatUtil
    									.format(new Date(), "yyyyMMddHHmmssSSS")
    									.substring(1))+"."+fileExtention;
    							finalFileName +=fileName+",";
    							// 定义上传路径
    							String imagePath = (String) BasePropertyConfigurer
    									.getContextProperty("idCardImageUploadPath");
    							String path = imagePath + fileName;
    							File localFile = new File(path);
    							file.transferTo(localFile);
    						} catch (Exception e) {
    							e.printStackTrace();
    						}
    					}
    				}
    			
    			}
    			JSONObject jsonObj = new JSONObject();
    			response.setContentType("text/html;charset=UTF-8");
    			jsonObj.put("success", true);
    			jsonObj.put("fileName",finalFileName);
    			returnJsonObject(response, jsonObj);
    		}
    	
    	} 


    --------------------------------------------------------------------------------------------
    上面介绍了原因,然后重点阐述了前后端的组件和实践方法,下面回归“图片上传问题”的本质,即流程问题。

    图片上传流程
    第一步:通过上传组件,选择一张或多张图片。
    第二步:确定上传,把图片传到后端,后端接收图片,保存到某个位置。
    第三步:前端提交表单,包括图片的名字等。

    这个问题有点复杂,再补充几点:
    第一:上传是可以同步的,也可以是异步的。
       比如通过form提交到后端,也可以是通过jquery等插件AJAX提交。
       异步提交的时候,需要后端“回显图片信息”,比如图片的名字、图片的URL。

    第二:是否允许图片存储位置,有“脏数据”,即没有实际价值的图片。
      比如,一个用户选择了图片,上传到了后端,但是表单可能没有提交。

      如果,直接把用户的图片,放到实际的存储位置,就有脏数据了,但不影响图片的展示。

      另外一种方式是,用户上传的图片,先存到一个特定的临时位置,用户确定上传图片后,把这里的图片移动到,实际的存储位置。

    第三:图片信息存到数据库。
      个人觉得,只存储图片的名字比较好,至于路径,存到数据库之后,会非常不灵活。
      前端展示图片,自定义url路径,比如/image/a.jpg,后端把/image路径映射到实际的图片位置。

    第四:图片的名字。
     把用户上传时的名字,作为实际存储的图片名字不好,一是中文容易出问题,二是不能保证唯一性,会存在覆盖的可能性。
     个人觉得,图片的名字用时间+随机数等方式生成唯一的名字,比如abcd.jpg。
     这个时候需要注意,图片的后缀,需要从用户上传的图片名字解析出来,比如从“小雷FansUnion.png”解析出后缀".png",把
    “小雷FansUnion.gif”存为“abcd.png”可能无法正常显示。

    第五:图片上传也可以使用云服务。
     2014年春,做ITFriend的时候,用的是“美图秀秀的图片上传组件”。云服务用着还是不错的,上传之后,可以立即进行“美化”“涂鸦”等操作。
    最终,再保存到数据库。

    第六:图片存储也可以使用云服务。
    个人觉得,图片一定要存储到Tomcat等服务器的外部。有的人为了方便,把图片存储到Tomcat的webapps目录,这样比较危险,tomcat重启-重新部署,可能会把图片给搞丢了。

    存储到硬盘等外部,做个图片请求映射就好。

    还有一种方式,图片存储也使用“云存储”,又拍云用着还凑合。
    -------------------------------------------------
    总结,图片上传其实是个很复杂的问题。
    没有足够经验,不懂实际需求的开发者,进度会估计不准。

    参考资料
    http://fex-team.github.io/webuploader/
    http://www.dropzonejs.com/
    http://blog.csdn.net/a1314517love/article/details/24183273 
    秒针工作时,BrandCenter项目积累
    朋来谷工作时,ITFriend项目积累
    一起好工作时,p2p项目积累
     
  • 相关阅读:
    中文编码问题
    TCP网络参数优化
    I/O的整体介绍
    TCP/IP
    Java序列化技术
    WEB请求过程(http解析,浏览器缓存机制,域名解析,cdn分发)
    λ(lambda)表达式
    HeapByteBuffer和DirectByteBuffer以及回收DirectByteBuffer
    锁的优化和注意事项
    java的NIO和AIO
  • 原文地址:https://www.cnblogs.com/qitian1/p/6462926.html
Copyright © 2011-2022 走看看