Java 單文件、多文件上傳 / 實現上傳進度條

博客地址:https://ainyi.com/76

日常,工作

在這裏總結一下上傳吧(是以前做過的練習,就彙總到個人博客吧)

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 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/>

多加了一個 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);
    }
}


進度條

要顯示上傳進度條,我這裏採用原生 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;
}

博客地址:https://ainyi.com/76

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章