在这里总结一下上传吧

java ssm 框架实现文件上传
实现:单文件上传、多文件上传(单选和多选),并且用 ajax 异步刷新,在当前界面显示上传的文件

后端

首先 springmvc 的配置文件要配置上传文件解析器:

<!-- 配置文件解析器 -->
<bean id="multipartResolver"
      class="org.springframework.web.multipart.commons.CommonsMultipartResolver"
      p:defaultEncoding="utf-8">
  <property name="uploadTempDir" value="/temp"></property>
  <property name="maxUploadSize">
    <value>209715200</value><!-- 200MB -->
  </property>
  <property name="maxInMemorySize">
    <value>4096</value><!-- 4KB大小读写 -->
  </property>
</bean>


其次在 pom.xml 中要配置上传文件的依赖

<dependency>
  <groupId>commons-io</groupId>
  <artifactId>commons-io</artifactId>
  <version>2.4</version>
</dependency>

<dependency>
  <groupId>commons-fileupload</groupId>
  <artifactId>commons-fileupload</artifactId>
  <version>1.3.1</version>
</dependency>

<dependency>
  <groupId>org.apache.commons</groupId>
  <artifactId>commons-lang3</artifactId>
  <version>3.3.2</version>
</dependency>


单文件上传

/**
 * 单文件上传
 * @param file
 * @param request
 * @return
 * @throws IllegalStateException
 * @throws IOException
 * @throws JSONException
 */
public static String simUpload(MultipartFile file, HttpServletRequest request) 
        throws IllegalStateException, IOException, JSONException{
    
    if(!file.isEmpty()){
        String path = request.getSession().getServletContext().getRealPath("/upload");
        //定义文件
        File parent = new File(path);
        if(!parent.exists()) parent.mkdirs();
        
        HashMap<String, Object> map = new HashMap<String,Object>();
        
        String oldName = file.getOriginalFilename();
        
        long size = file.getSize();
        
        //使用TmFileUtil文件上传工具获取文件的各种信息
        //优化文件大小
        String sizeString = TmFileUtil.countFileSize(size);
        //获取文件后缀名
        String ext = TmFileUtil.getExtNoPoint(oldName);
        //随机重命名,10位时间字符串
        String newFileName = TmFileUtil.generateFileName(oldName, 10, "yyyyMMddHHmmss");
        
        String url = "upload/"+newFileName;
        
        //文件传输,parent文件
        file.transferTo(new File(parent, newFileName));
        
        map.put("oldname",oldName);//文件原名称
        map.put("ext",ext);
        map.put("size",sizeString);
        map.put("name",newFileName);//文件新名称
        map.put("url",url);
        
        //以json方式输出到页面
        return JSONUtil.serialize(map);
    }else{
        return null;
    }
}
/**
 * 单文件上传
 * @param file
 * @param request
 * @return
 * @throws IllegalStateException
 * @throws IOException
 * @throws JSONException
 */
public static String simUpload(MultipartFile file, HttpServletRequest request) 
        throws IllegalStateException, IOException, JSONException{
    
    if(!file.isEmpty()){
        String path = request.getSession().getServletContext().getRealPath("/upload");
        //定义文件
        File parent = new File(path);
        if(!parent.exists()) parent.mkdirs();
        
        HashMap<String, Object> map = new HashMap<String,Object>();
        
        String oldName = file.getOriginalFilename();
        
        long size = file.getSize();
        
        //使用TmFileUtil文件上传工具获取文件的各种信息
        //优化文件大小
        String sizeString = TmFileUtil.countFileSize(size);
        //获取文件后缀名
        String ext = TmFileUtil.getExtNoPoint(oldName);
        //随机重命名,10位时间字符串
        String newFileName = TmFileUtil.generateFileName(oldName, 10, "yyyyMMddHHmmss");
        
        String url = "upload/"+newFileName;
        
        //文件传输,parent文件
        file.transferTo(new File(parent, newFileName));
        
        map.put("oldname",oldName);//文件原名称
        map.put("ext",ext);
        map.put("size",sizeString);
        map.put("name",newFileName);//文件新名称
        map.put("url",url);
        
        //以json方式输出到页面
        return JSONUtil.serialize(map);
    }else{
        return null;
    }
}


多文件上传(整合了==单选文件==和==多选文件==的两种)

/**
 * 多文件上传
 * @param files
 * @param request
 * @return
 * @throws IllegalStateException
 * @throws IOException
 * @throws JSONException
 */
public static List<HashMap<String, Object>> mutlUpload(MultipartFile[] files, HttpServletRequest request) 
        throws IllegalStateException, IOException, JSONException{
    
    if(files.length > 0){
        String path = request.getSession().getServletContext().getRealPath("/upload");
        //定义文件
        File parent = new File(path);
        if(!parent.exists()) parent.mkdirs();
        
        //创建这个集合保存所有文件的信息
        List<HashMap<String, Object>> listMap = new ArrayList<HashMap<String, Object>>();
        
        //循环多次上传多个文件
        for (MultipartFile file : files) {
            
            //创建map对象保存每一个文件的信息
            HashMap<String, Object> map = new HashMap<String,Object>();
            
            String oldName = file.getOriginalFilename();

            long size = file.getSize();
            
            //使用TmFileUtil文件上传工具获取文件的各种信息
            //优化文件大小
            String sizeString = TmFileUtil.countFileSize(size);
            //获取文件后缀名
            String ext = TmFileUtil.getExtNoPoint(oldName);
            //随机重命名,10位时间字符串
            String newFileName = TmFileUtil.generateFileName(oldName, 10, "yyyyMMddHHmmss");
            
            String url = "upload/"+newFileName;
            
            //文件传输,parent文件
            file.transferTo(new File(parent, newFileName));
            
            map.put("oldname",oldName);//文件原名称
            map.put("ext",ext);
            map.put("size",sizeString);
            map.put("name",newFileName);//文件新名称
            map.put("url",url);
            
            listMap.add(map);
        }
        
        //以json方式输出到页面
        return listMap;
    }else{
        return null;
    }
}
/**
 * 多文件上传
 * @param files
 * @param request
 * @return
 * @throws IllegalStateException
 * @throws IOException
 * @throws JSONException
 */
public static List<HashMap<String, Object>> mutlUpload(MultipartFile[] files, HttpServletRequest request) 
        throws IllegalStateException, IOException, JSONException{
    
    if(files.length > 0){
        String path = request.getSession().getServletContext().getRealPath("/upload");
        //定义文件
        File parent = new File(path);
        if(!parent.exists()) parent.mkdirs();
        
        //创建这个集合保存所有文件的信息
        List<HashMap<String, Object>> listMap = new ArrayList<HashMap<String, Object>>();
        
        //循环多次上传多个文件
        for (MultipartFile file : files) {
            
            //创建map对象保存每一个文件的信息
            HashMap<String, Object> map = new HashMap<String,Object>();
            
            String oldName = file.getOriginalFilename();

            long size = file.getSize();
            
            //使用TmFileUtil文件上传工具获取文件的各种信息
            //优化文件大小
            String sizeString = TmFileUtil.countFileSize(size);
            //获取文件后缀名
            String ext = TmFileUtil.getExtNoPoint(oldName);
            //随机重命名,10位时间字符串
            String newFileName = TmFileUtil.generateFileName(oldName, 10, "yyyyMMddHHmmss");
            
            String url = "upload/"+newFileName;
            
            //文件传输,parent文件
            file.transferTo(new File(parent, newFileName));
            
            map.put("oldname",oldName);//文件原名称
            map.put("ext",ext);
            map.put("size",sizeString);
            map.put("name",newFileName);//文件新名称
            map.put("url",url);
            
            listMap.add(map);
        }
        
        //以json方式输出到页面
        return listMap;
    }else{
        return null;
    }
}

前端

前端代码:
文件多选,实际上在

<input type="file" 
       name="fileupmulti"
       accept="image/jpeg,image/png"
       onchange="mutiFiles(this)"
       multiple/>
<input type="file" 
       name="fileupmulti"
       accept="image/jpeg,image/png"
       onchange="mutiFiles(this)"
       multiple/>

多加了一个 multiple 属性

onchange 事件代码

// 单文件上传
function uploadFile(obj){
  // 创建一个 FormData 对象,用一些键值对来模拟一系列表单控件
  // 即把 form 中所有表单元素的 name 与 value 组装成一个 queryString
  let form = new FormData();
  let fileObj = obj.files[0];
  form.append('doc',fileObj);
  
  // ajax 代码...
}

// 多文件上传(多选)
function mutiFiles(obj){
  let form = new FormData();
  let fileObj = obj.files;
  let length = fileObj.length;
  // 将 fileObj 转换成数组
  // let filese = Array.from(fileObj);
  for(let i = 0; i < length; i++){
    form.append('doc', fileObj[i]);
  }

  // ajax 代码...
}

// 多文件上传(单选:一个一个选择文件,最后点击提交按钮触发的方法)
function multipartone(){
  let file1 = $('.fileupon11').get(0).files[0];
  let file2 = $('.fileupon12').get(0).files[0];
  let file3 = $('.fileupon13').get(0).files[0];
  //如果都是空,则直接退出
  isEmpty(file1) && isEmpty(file2) && isEmpty(file3) return;
        
  let form = new FormData();
  //用同一个名字,注入到controller层的参数数组
  form.append('doc', file1);
  form.append('doc', file2);
  form.append('doc', file3);
  
  // ajax 代码...
}

要想在当前界面显示上传的文件,而不跳转,就利用 ajax 异步请求

不过需要注意的是,我这里使用 FormData() 储存文件对象, ajax 要配上这几个参数才可实现文件上传:

$.ajax({
 type: "post",
 data: form,  // FormData()对象
  url: basePath+"/upload/mutl",
 contentType: false, // 必须false才会自动加上正确的Content-Type
 processData: false, // 必须false才会避开 jQuery 对 formdata 的默认处理, XMLHttpRequest会对 formdata 进行正确的处理
 success: function(data){
    // TODO
 }
})

controller 层调用

package com.krry.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.json.JSONException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.krry.util.UploadUtil;

/**
 * 文件上传类
 * KrryUploadController
 * @author krry
 * @version 1.0.0
 *
 */
@Controller
@RequestMapping("/upload")
public class KrryUploadController {
    
    /**
     * 单文件上传
     * @param file
     * @param request
     * @return
     * @throws IllegalStateException
     * @throws IOException
     * @throws JSONException
     */
    @ResponseBody
    @RequestMapping(value = "/file")
    public String krryupload(@RequestParam("doc") MultipartFile file, HttpServletRequest request) throws IllegalStateException, IOException, JSONException{

        //调用工具类完成上传,返回相关数据到页面
        return UploadUtil.simUpload(file, request);
    }
    
    /**
     * 多文件上传
     * @param file
     * @param request
     * @return
     * @throws IllegalStateException
     * @throws IOException
     * @throws JSONException
     */
  // 这里的MultipartFile[] file表示前端页面上传过来的多个文件,file对应页面中多个file类型的input标签的name,但框架只会将一个文件封装进一个MultipartFile对象,
  // 并不会将多个文件封装进一个MultipartFile[]数组,直接使用会报[Lorg.springframework.web.multipart.MultipartFile;.<init>()错误,
  // 所以需要用@RequestParam校正参数(参数名与MultipartFile对象名一致),当然也可以这么写:@RequestParam("file") MultipartFile[] files。
    @ResponseBody
    @RequestMapping(value = "/mutl")
    public List<HashMap<String, Object>> krryuploadMutl(@RequestParam("doc") MultipartFile[] file, HttpServletRequest request) throws IllegalStateException, IOException, JSONException{
        //调用工具类完成上传,返回相关数据到页面
        return UploadUtil.mutlUpload(file, request);
    }
}
package com.krry.controller;

import java.io.IOException;
import java.util.HashMap;
import java.util.List;

import javax.servlet.http.HttpServletRequest;

import org.apache.struts2.json.JSONException;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;

import com.krry.util.UploadUtil;

/**
 * 文件上传类
 * KrryUploadController
 * @author krry
 * @version 1.0.0
 *
 */
@Controller
@RequestMapping("/upload")
public class KrryUploadController {
    
    /**
     * 单文件上传
     * @param file
     * @param request
     * @return
     * @throws IllegalStateException
     * @throws IOException
     * @throws JSONException
     */
    @ResponseBody
    @RequestMapping(value = "/file")
    public String krryupload(@RequestParam("doc") MultipartFile file, HttpServletRequest request) throws IllegalStateException, IOException, JSONException{

        //调用工具类完成上传,返回相关数据到页面
        return UploadUtil.simUpload(file, request);
    }
    
    /**
     * 多文件上传
     * @param file
     * @param request
     * @return
     * @throws IllegalStateException
     * @throws IOException
     * @throws JSONException
     */
  // 这里的MultipartFile[] file表示前端页面上传过来的多个文件,file对应页面中多个file类型的input标签的name,但框架只会将一个文件封装进一个MultipartFile对象,
  // 并不会将多个文件封装进一个MultipartFile[]数组,直接使用会报[Lorg.springframework.web.multipart.MultipartFile;.<init>()错误,
  // 所以需要用@RequestParam校正参数(参数名与MultipartFile对象名一致),当然也可以这么写:@RequestParam("file") MultipartFile[] files。
    @ResponseBody
    @RequestMapping(value = "/mutl")
    public List<HashMap<String, Object>> krryuploadMutl(@RequestParam("doc") MultipartFile[] file, HttpServletRequest request) throws IllegalStateException, IOException, JSONException{
        //调用工具类完成上传,返回相关数据到页面
        return UploadUtil.mutlUpload(file, request);
    }
}

进度条

要显示上传进度条,我这里采用原生 ajax 方法

function uploadFile(obj) {
  // ...
  // 一些获取上传对象的相关代码

  // 创建一个 ajax 对象
  var xhr = new XMLHttpRequest();

  // 规定请求的类型、URL 以及是否异步处理请求。true为异步
  // 请求是异步的。因为要实时获取到上传的进度,则请求需是异步的,如果是同步的话,会直到请求完成才能获取到响应
  xhr.open("post", basePath+"/upload/file", true);

  // 上传成功进入的回调函数
  xhr.onreadystatechange = function(){
  if(xhr.readyState==4 && xhr.status==200){ // 状态 4 和 200 代表和服务器端交互成功
    // 获取上传成功的返回数据
    var data = xhr.responseText.trim();
    jdata = eval("("+data+")");
    krry_uploadsuccess(jdata);
  }
  };
  // 监听文件上传的进度
  xhr.upload.addEventListener("progress", progressFunction, false);
  // 发送http请求:将请求发送到服务器,与后台交互
  xhr.send(form);
}


// 上传进度的回调函数
function progressFunction(event) {
  let prograssbarDom = document.getElementById("prograssbar");
  let fileRea = document.getElementById("fileRea");
  if (prograssbarDom && event.lengthComputable) {
    let percent = event.loaded / event.total; //文件上传进度百分比
    let p = Math.floor(percent*100);
    prograssbarDom.style.width = p+"%";
    fileRea.innerHTML = p+"%";
  }
}

附上优化文件大小的代码:

/**
 * 将文件的字节数转换成文件的大小
 * com.krry.uitl 
 * 方法名:format
 * @author krry 
 * @param size
 * @return String
 * @exception 
 * @since  1.0.0
 */
public static String format(long size){
    float fsize = size;
    String fileSizeString;
    if (fsize < 1024) {
        fileSizeString = String.format("%.2f", fsize) + "B"; //2f表示保留两位小数
    } else if (fsize < 1048576) {
        fileSizeString = String.format("%.2f", fsize/1024) + "KB";
    } else if (fsize < 1073741824) {
        fileSizeString = String.format("%.2f", fsize/1024/1024) + "MB";
    } else if (fsize < 1024 * 1024 * 1024) {
        fileSizeString = String.format("%.2f", fsize/1024/1024/1024) + "GB";
    } else {
        fileSizeString = "0B";
    }
    return fileSizeString;
}
/**
 * 将文件的字节数转换成文件的大小
 * com.krry.uitl 
 * 方法名:format
 * @author krry 
 * @param size
 * @return String
 * @exception 
 * @since  1.0.0
 */
public static String format(long size){
    float fsize = size;
    String fileSizeString;
    if (fsize < 1024) {
        fileSizeString = String.format("%.2f", fsize) + "B"; //2f表示保留两位小数
    } else if (fsize < 1048576) {
        fileSizeString = String.format("%.2f", fsize/1024) + "KB";
    } else if (fsize < 1073741824) {
        fileSizeString = String.format("%.2f", fsize/1024/1024) + "MB";
    } else if (fsize < 1024 * 1024 * 1024) {
        fileSizeString = String.format("%.2f", fsize/1024/1024/1024) + "GB";
    } else {
        fileSizeString = "0B";
    }
    return fileSizeString;
}