整合营销服务商

电脑端+手机端+微信端=数据同步管理

免费咨询热线:

SpringMVC批量上传图片,实现上传前图片预览

SpringMVC批量上传图片,实现上传前图片预览

近有个功能需要实现批量上传图片,然后实现图片预览,因为项目比较老,同时对界面和用户操作体验也要求不太高,就没去找网上的开源插件,直接写了个简单的功能,这里做个记录备份

因为这个是实验性的小代码,就没做太多的验证和界面调整


可以实现乱序删除

这就是简单的功能界面,下面就是代码

首先是SpringMVC的xm需要配置下


    <!-- 配置MultipartResolver 用于文件上传 使用spring的CommosMultipartResolver 说明: p:defaultEncoding="UTF-8":这里设置默认的文件编码为UTF-8,必须与用户JSP的默认编码一致; 
        p:maxUploadSize="5000000":指定文件上传大小,单位为字节; p:uploadTempDir="fileUpload/temp":文件上传临时目录,上传完成后,就会将临时文件删除; -->
    <bean id="multipartResolver"
        class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
        p:defaultEncoding="UTF-8" p:maxUploadSize="5000000" p:uploadTempDir="fileUpload/temp">
    </bean>

JSP+js

<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<%
String path=request.getContextPath();
String basePath=request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
response.setHeader("Cache-Control","no-store");//HTTP 1.1  
response.setHeader("Pragma","no-cache");//HTTP 1.0  
response.setDateHeader("Expires",0);//prevents caching at the proxy server  
%>

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
  <head>
    <base href="<%=basePath%>">
    
    <title>My JSP 'batchFileUpload.jsp' starting page</title>
    
    <meta http-equiv="pragma" content="no-cache">
    <meta http-equiv="cache-control" content="no-cache">
    <meta http-equiv="expires" content="0">    
    <meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
    <meta http-equiv="description" content="This is my page">
    
    <link rel="stylesheet" type="text/css" href="<%=basePath%>static/css/webuploader.css"/>
    <script type="text/javascript" src="<%=basePath%>static/js/jquery-1.9.1.min.js"></script>
    <script type="text/javascript" src="<%=basePath%>static/js/ajaxfileupload.js"></script>
    <script type="text/javascript" src="<%=basePath%>static/js/bootstrap.js"></script>
    <script type="text/javascript" src="<%=basePath%>static/js/jquery.json-2.4.js" charset="UTF-8"></script> 
    <script type="text/javascript" src="<%=basePath%>static/js/jquery.validate.js"></script> 
    <script type="text/javascript" src="<%=basePath%>static/js/jquery.Jcrop.js"></script>
    <script type="text/javascript" src="<%=basePath%>static/js/webuploader.nolog.js"></script>
    
    <script type="text/javascript">
        $(function(){
            //动态打开file标签
            $("#changeImg").click(function(){
                var files=document.getElementsByName("file");
                for(var i=0;i<files.length;i++)
                {
                    if($(files[i]).val()=='')
                    {
                        $(files[i]).click();
                        break;
                    }
                }
            });
        }); 
        
        //预览图事件
        function readURL(preFile,markId){
            var reader=new FileReader();
            reader.readAsDataURL(preFile.files[0]); 
            reader.onload=function(e){
                //循环判断为空
                var preImges=document.getElementsByName("preImg");
                for(var i=0;i<preImges.length;i++){
                    if($(preImges[i]).attr("src")==''){
                        $(preImges[i]).removeAttr("src");
                        $(preImges[i]).removeAttr("markId");
                        $(preImges[i]).attr("src",e.target.result);
                        $(preImges[i]).attr("markId",markId);
                        break;
                    }
                }

                
            }
            
        }
    
        //删除预览图事件
        function deletePre(preId){
            $("#"+preId).removeAttr("src");
            $("#"+preId).attr("src",'');
            var tempMark=$("#"+preId).attr("markId");
            $("#"+tempMark).val('');
        }
    </script>

  </head>
  
  <body>
    <div style="margin-left: 35%;margin-top: 10%">
        <div  style="margin-left:5%;margin-top: 10%">简单的照片预览与批量上传</div>
        <form action=""  method="post" enctype="multipart/form-data" id="imgFile">
            <table cellpadding="10px" cellspacing="10px">
                <tbody>
                    <tr>
                        <td>用户名:</td>
                        <td><input id="userName" name="userName" type="text"></td>
                        <td> </td>
                    </tr>               
                    <tr>
                        <td>相册:</td>
                        <td> </td>
                        <td> </td>
                    </tr>
                    <tr>
                        <td><img alt="相片1" src="" style="width: 100px" id="preImg_1" name="preImg"><a href="javascript:void(0);"onclick="deletePre('preImg_1')">删除</a></td>
                        <td><img alt="相片2" src="" style="width: 100px" id="preImg_2" name="preImg"><a href="javascript:void(0);"onclick="deletePre('preImg_2')">删除</a></td>
                        <td><img alt="相片3" src="" style="width: 100px" id="preImg_3" name="preImg"><a href="javascript:void(0);"onclick="deletePre('preImg_3')">删除</a></td>
                    </tr>
                </tbody>
            </table>
            <div  style="margin-left:8%;margin-top:5%">
                <input type="button" value="选择图片" id="changeImg">  <input type="submit" value="     提  交     ">
            </div>
            <div>
                <span>实际提交相片的File组件</span><br>
                <br><br>
                <input type="file" id="fileId_1" name="file" onchange="readURL(this,'fileId_1')">
                <input type="file" id="fileId_2" name="file" onchange="readURL(this,'fileId_2')">
                <input type="file" id="fileId_3" name="file" onchange="readURL(this,'fileId_3')">
            </div>
        </form>
    
    </div>
  </body>
</html>

后台代码

package com.lovo.controller;

import java.io.File;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.log4j.Logger;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import com.lovo.utils.FileUploadCheck;

@Controller
public class batchImgController {

    private static Logger logger=Logger.getLogger(batchImgController.class);
    
    @RequestMapping(value="/batchImg.do",method=RequestMethod.GET)
    public String batchImgGet(){
        //get方法,处理jsp跳转前的一些验证和准备
        
        return "batchFileUpload";
        
    }
    
    /**
     * 图片批量提交方法
     * @param userName
     * @param file
     * @return
     */
    @RequestMapping(value="/batchImg.do",method=RequestMethod.POST)
    public String batchImgPost(@RequestParam("userName") String userName,@RequestParam("file")MultipartFile[] file ,HttpServletRequest request){
        
        //表单基本信息
        System.out.println(userName);
        
        // 文件保存路径  
        String filePath=request.getSession().getServletContext().getRealPath("/") + "fileUpload/";
        //储存文件名或文件路径
        List<String> imgNameList=new ArrayList<String>();
        
        try {
            for (MultipartFile img : file)
            {
                if(!img.isEmpty())
                {
                    //文件重命名
                    Date day=new Date();
                    SimpleDateFormat sdf=new SimpleDateFormat("yyyyMMdd");
                    String newName=sdf.format(day)+System.currentTimeMillis()+".jpg";
                    
                    //方法判定是否为图片
                    if(FileUploadCheck.allowUpload(img.getContentType()))
                    {
                        img.transferTo(new File(filePath+newName));
                        
                        //存储文件的新名字,之后根据项目情况对文件进行入库,并把实体文件上传到FTP
                        imgNameList.add(newName);
                    }
                    
                }
            }
        } catch (Exception e) {
            logger.error("文件上传失败");
        }
        
        return "batchFileUpload";
        
    };
}

工具类

pring MVC 的请求处理流程

DispatcherServlet

DispatcherServlet继承结构

DispatcherServlet对请求的处理流程

大致步骤

  • 绑定?些 Attribute
    • WebApplicationContext / LocaleResolver / ThemeResolver
  • 处理 Multipart
    • 如果是,则将请求转为 MultipartHttpServletRequest
  • Handler 处理
    • 如果找到对应 Handler,执? Controller 及前后置处理器逻辑
  • 处理返回的 Model ,呈现(render)视图

具体可以参见DispatcherServlet#doService(...)和DispatcherServlet#doDispatch(...)方法

如何定义处理?法(即Handler方法)

定义映射关系

  • @Controller
  • @RequestMapping
    • value: 指定请求的实际地址, 比如 /action/info之类
    • method: 指定请求的method类型, GET、POST、PUT、DELETE等
    • consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
    • produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回
    • params: 指定request中必须包含某些参数值是,才让该方法处理
    • headers: 指定request中必须包含/不包含某些指定的header值,才能让该方法处理请求
  • @RestController
  • @GetMapping / @PostMapping / @PutMapping / @DeleteMapping / @PatchMapping

定义处理?法

  • @RequestBody / @ResponseBody / @ResponseStatus(用于改变HTTP响应的状态码)
  • @PathVariable / @RequestParam / @RequestHeader
    • 通过@PathVariable 可以将URL中占位符参数{xxx}绑定到处理器类的方法形参中@PathVariable(“xxx“)
    • @RequestParam标注在接口的方法参数上,被标注的参数的值来源于request.getParameter或request.getParameterValues
    • @RequestHeader注解,能够将请求头中的变量值映射到控制器的参数中。
  • HttpEntity / RequestEntity / ResponseEntity
    • HttpEntity表示http的request和resposne实体,它由消息头和消息体组成。从HttpEntity中可以获取http请求头和回应头,也可以获取http请求体和回应体信息。HttpEntity的典型应用是配合RestTemplate。

详细参数

https://docs.spring.io/spring-framework/docs/5.1.5.RELEASE/spring-framework-reference/web.html#mvc-ann-arguments

详细返回

https://docs.spring.io/spring-framework/docs/5.1.5.RELEASE/spring-framework-reference/web.html#mvc-ann-return-types

?法示例

定义类型转换

SpringBoot的默认实现

在WebMvcAutoConfiguration的内部类WebMvcAutoConfigurationAdapter中实现了重写WebMvcConfigurer接口这样一个方法。

addFormatters方法用于添加默认的{@link Converter Converter}和{@link Formatter Formatters}注册中心。

ApplicationConversionService#addBeans(...)方法

Converter和Formatter的异同

两者的作用一样,都是类型转换。

org.springframework.format.Formatter只能做String类型到其他类型的转换。

org.springframework.core.convert.converter.Converter可以做任意类型的转换。

??实现 WebMvcConfigurer

  • 添加?定义的 Converter
  • 添加?定义的 Formatter

定义校验

  • 通过 Validator 对绑定结果进?校验
    • Hibernate Validator
  • @Valid、@Validated注解 及自定义注解校验
  • BindingResult (对于不希望Spring MVC来介入我的Valid失败后的操作,可以通过BindingResult实现)

Multipart 上传

  • 配置 MultipartResolver
    • Spring Boot ?动配置 MultipartAutoConfiguration
  • ?持类型 multipart/form-data
  • MultipartFile 类型

一些误区/注意点

@RequestParam注解能读取请求体里的内容吗?

@RequestParam:In Spring MVC, "request parameters" map to query parameters, form data, and parts in multipart requests.

有时候我们只想取出请求body里面的部分内容,但@RequstBody注解是把整个请求的Body传给参数,而RequestParam是可以把Body里的对应部分取出来传给参数。

controller里面可以实现传多个对象么? 比如:hello(Student student, Teacher teacher)

注意:@RequestBody只能将一个请求报文体转成一个复杂对象。

  1. 将多个对象合成一个中间复杂对象,之后再拆开。
  2. 通过 Map 或者Jackson 的 ObjectNode
  3. 自定义注解,并将它注册到 Spring MVC

TML 帮助器用于修改 HTML 输出。


HTML 帮助器

通过 MVC,HTML 帮助器类似于传统的 ASP.NET Web Form 控件。

就像 ASP.NET 中的 Web Form 控件,HTML 帮助器用于修改 HTML。但是 HTML 帮助器是更轻量级的。与 Web Form 控件不同,HTML 帮助器没有事件模型和视图状态。

在大多数情况下,HTML 帮助器仅仅是一个返回字符串的方法。

通过 MVC,您可以创建您自己的帮助器,或者直接使用内建的 HTML 帮助器。


标准的 HTML 帮助器

MVC 包含了大多数常用的 HTML 元素类型的标准帮助器,比如 HTML 链接和 HTML 表单元素。


HTML 链接

呈现 HTML 链接的最简单的方法是使用 HTML.ActionLink() 帮助器。

通过 MVC,Html.ActionLink() 不连接到视图。它创建一个连接到控制器操作。

Razor 语法:

@Html.ActionLink("About this Website", "About")

ASP 语法:

<%=Html.ActionLink("About this Website", "About")%>

第一个参数是链接文本,第二个参数是控制器操作的名称。

上面的 Html.ActionLink() 帮助器,输出以下的 HTML:

<a href="/Home/About">About this Website</a>

Html.ActionLink() 帮助器的一些属性:

属性描述
.linkTextURL 文本(标签),定位点元素的内部文本。
.actionName操作(action)的名称。
.routeValues传递给操作(action)的值,是一个包含路由参数的对象。
.controllerName控制器的名称。
.htmlAttributesURL 的属性设置,是一个包含要为该元素设置的 HTML 特性的对象。
.protocolURL 协议,如 "http" 或 "https"。
.hostnameURL 的主机名。
.fragmentURL 片段名称(定位点名称)。

注释:您可以向控制器操作传递值。例如,您可以向数据库 Edit 操作传递数据库记录的 id:

Razor 语法 C#:

@Html.ActionLink("Edit Record", "Edit", new {Id=3})

Razor 语法 VB:

@Html.ActionLink("Edit Record", "Edit", New With{.Id=3})

上面的 Html.ActionLink() 帮助器,输出以下的 HTML:

<a href="/Home/Edit/3">Edit Record</a>


HTML 表单元素

以下 HTML 帮助器可用于呈现(修改和输出)HTML 表单元素:

  • BeginForm()

  • EndForm()

  • TextArea()

  • TextBox()

  • CheckBox()

  • RadioButton()

  • ListBox()

  • DropDownList()

  • Hidden()

  • Password()

ASP.NET 语法 C#:

<%=Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %>

<% using (Html.BeginForm()){%>

<p>

<label for="FirstName">First Name:</label>

<%=Html.TextBox("FirstName") %>

<%=Html.ValidationMessage("FirstName", "*") %>

</p>

<p>

<label for="LastName">Last Name:</label>

<%=Html.TextBox("LastName") %>

<%=Html.ValidationMessage("LastName", "*") %>

</p>

<p>

<label for="Password">Password:</label>

<%=Html.Password("Password") %>

<%=Html.ValidationMessage("Password", "*") %>

</p>

<p>

<label for="Password">Confirm Password:</label>

<%=Html.Password("ConfirmPassword") %>

<%=Html.ValidationMessage("ConfirmPassword", "*") %>

</p>

<p>

<label for="Profile">Profile:</label>

<%=Html.TextArea("Profile", new {cols=60, rows=10})%>

</p>

<p>

<%=Html.CheckBox("ReceiveNewsletter") %>

<label for="ReceiveNewsletter" style="display:inline">Receive Newsletter?</label>

</p>

<p>

<input type="submit" value="Register" />

</p>

<%}%>