首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

SpringMVC 異步文件上傳實戰:基於 ajaxFileUpload.js 與 iframe 方案詳解

     編輯:Java 綜合教學

SpringMVC 異步文件上傳實戰指南

在傳統 Web 開發中,文件上傳往往伴隨著頁面刷新,用戶體驗較差。本文詳細介紹如何在 SpringMVC + MyBatis 架構下,利用 ajaxFileUpload.js 插件實現表單數據與文件的「異步同時上傳」,並解決高版本 jQuery 兼容性、表單序列化為 JSON 等常見坑點。

一、 技術方案概覽

  • 核心插件:ajaxFileUpload.js(底層通過動態創建 <iframe> 模擬異步提交)
  • 輔助插件:formToJson.js(將表單數據序列化為 JSON 對象)
  • 後端框架:SpringMVC + MyBatis,控制器接收 MultipartFile 及表單綁定對象
  • 關鍵點:表單 enctype="multipart/form-data"、文件域 id 與 JS 配置一致、後端事務管理

二、 前端頁面佈局與 JS 邏輯

1. JSP 表單結構(關鍵屬性)

<form:form id="addOrUpdateModalForm" method="post" 
    enctype="multipart/form-data" modelAttribute="userInfo">
    
    <!-- 隱藏域綁定 ID 等 -->
    <form:hidden path="userId" />
    <form:hidden path="imageUrl" />

    <!-- 文件上傳域:id 必須與 JS 中 fileElementId 一致 -->
    <input type="file" id="userImage" name="userImage" 
        accept="image/*" onchange="upfileName.value=this.value" />
    <input type="text" id="upfileName" readonly />
    <input type="button" value="選擇圖片" onclick="userImage.click()" />

    <!-- 其他業務字段:姓名、工號、部門、崗位等 -->
    <form:input path="name" />
    <form:select path="departmentId"><form:options /></form:select>
    <!-- ... 省略其餘字段 -->
</form:form>

2. 核心 JS:表單驗證、序列化、異步提交

function saveOrUpdateForm() {
    // 1. 圖片格式校驗 & 表單驗證
    if (!checkImageFileType() || !$('#addOrUpdateModalForm').validationAjax()) {
        return;
    }

    var postUrl = appCtx + "userInfo/insertOrUpdate.do";
    // 2. 關鍵:將表單序列化為 JSON 對象
    var params = $('#addOrUpdateModalForm').serializeJson(); 

    $('#loading').modal('show');

    // 3. 調用 ajaxFileUpload 核心方法
    $.ajaxFileUpload({
        url: postUrl,
        secureuri: false,
        fileElementId: 'userImage', // 對應 input file 的 id
        dataType: 'json',
        data: params, // 附帶的表單 JSON 數據
        success: function(data, status) {
            // 成功回調:關閉遮罩、彈窗、刷新列表
            $('#loading').modal('hide');
            doSuccess(); 
            successModalStatus(...);
            succesListFormReflush();
        },
        error: function(data, status, e) {
            console.error("上傳失敗:", e);
            $('#loading').modal('hide');
        }
    });
    return false; // 阻止默認表單提交
}

3. 解決 jQuery 高版本移除 handleError 問題

ajaxFileUpload.js 內部依賴 jQuery.handleError,該方法在 jQuery 1.9+ 被移除。解決方案:在項目加載的 JS 中手動擴展:

// 放在 jquery.min.js 之後,ajaxFileUpload.js 之前
jQuery.extend({
    handleError: function(s, xhr, status, e) {
        if (s.error) s.error.call(s.context || s, xhr, status, e);
        if (s.global) $(s.context || document).trigger("ajaxError", [xhr, s, e]);
    }
});

三、 後端控制器實現

@Controller
@RequestMapping("/userInfo")
public class UserInfoController {

    @Autowired private UserInfoService userInfoService;
    @Autowired private UserPositionService userPositionService;

    @RequestMapping("insertOrUpdate")
    @ResponseBody
    public ActionResult insertOrUpdate(
            UserInfo userInfo, 
            HttpSession session,
            @RequestParam(value="userImage", required=false) MultipartFile userImage) {

        ActionResult result = new ActionResult();
        try {
            // 1. 判斷新增/修改
            boolean isAdd = (userInfo.getUserId() == null);
            
            // 2. 文件上傳處理(新增與修改邏輯略有不同)
            String savePath = session.getServletContext().getRealPath("/resources/images/userImage");
            String fileName = handleFileUpload(userImage, savePath, isAdd, userInfo);
            if (fileName != null) userInfo.setImageUrl(fileName);

            // 3. 業務邏輯:保存用戶 & 維護用戶-崗位關係表
            if (isAdd) {
                userInfo.setUserUuid(UUIDUtil.getUUID());
                userInfo.setPassword("123456"); // 默認密碼
                userInfoService.insertSelective(userInfo);
                // 插入崗位關係
                insertOrUpdatePositionRel(userInfo.getUserId(), userInfo.getPositionId());
            } else {
                // 修改邏輯:刪除舊圖片、更新用戶、同步崗位關係
                deleteOldImageIfNeeded(userInfo, savePath);
                userInfoService.updateByPrimaryKeySelective(userInfo);
                insertOrUpdatePositionRel(userInfo.getUserId(), userInfo.getPositionId());
            }

            // 4. 日誌記錄 (SLF4J + MDC)
            logOperation(session, isAdd, userInfo);

            result.setResult(OPERATOR_RESULT_SUCCESS_KEY);
            result.setMessage(isAdd ? OPERATOR_RESULT_SUCCESS_ADD_VALUE : OPERATOR_RESULT_SUCCESS_UPDATE_VALUE);

        } catch (Exception e) {
            e.printStackTrace();
            result.setResult(OPERATOR_RESULT_ERROR_KEY);
            result.setMessage(OPERATOR_RESULT_ERROR_VALUE);
        }
        return result;
    }

    // --- 私有輔助方法:文件處理、崗位關係維護、日誌記錄 ---
    // 代碼省略,核心邏輯見原文 controller 完整代碼
}

四、 關鍵配置與依賴

1. SpringMVC 文件上傳解析器配置

<!-- spring-mvc.xml -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <property name="maxUploadSize" value="10485760" /> <!-- 10MB -->
    <property name="defaultEncoding" value="UTF-8" />
</bean>

2. Maven 核心依賴

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.3</version>
</dependency>
<dependency>
    <groupId>commons-io</groupId>
    <artifactId>commons-io</artifactId>
    <version>2.6</version>
</dependency>

五、 總結與最佳實踐建議

  1. 插件選型:ajaxFileUpload.js 屬於「偽異步」(iframe 方案),現代項目建議遷移至 FormData + XMLHttpRequest/fetch 原生方案,或使用 jQuery.ajax 配合 processData: false, contentType: false。
  2. 安全性:後端務必校驗文件類型、大小,防止惡意腳本上傳;文件名使用時間戳+隨機數重命名,避免覆蓋與路徑遍歷。
  3. 事務控制:用戶信息保存與崗位關係維護應在同一事務中(@Transactional),保證數據一致性。
  4. 體驗優化:上傳前顯示 Loading 遮罩,進度條反饋(iframe 方案難以實現真進度條),失敗友好提示。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved