?1、前端 Vue3
QualityFileInfoDialog.vue
<script setup lang="ts" name="QualityFile">
......
// 上傳,防抖
const onUploadClick = debounce(() => {// 模擬點擊元素if (fileInputRef.value) {// 重置以允許重復選擇相同文件fileInputRef.value.value = "";fileInputRef.value.click();}},1000,{ leading: true, trailing: true, maxWait: 1000 }
);// 點擊【上傳】觸發,實現 SQL Server image 類型文件上傳
const handleUpload = async (e: Event) => {// 清空 FormData 表單數據的內容:重新賦值,創建新實例,舊數據被丟棄(完全清空),需要使用 let 聲明對象,不能使用 const 聲明對象formData = new FormData();// 獲取文件對象const input = e.target as HTMLInputElement;if (!input.files?.length) return;const file = input.files[0];// 校驗文件大小if (file.size > 1024 * 1024 * 10) {ElMessage.warning("文件大小不能超過10MB");return;}if (file) {// 獲取文件名的擴展名(后綴)extension.value = getExtension(file.name);if (upperCase(extension.value) === upperCase("pdf")) {// 將文件對象 file 添加到 formData 對象中,uploadFile 需要與后端接口中接收文件的參數名一致,如果不一致,則后端需要指定參數名,如 @RequestPart("uploadFile") MultipartFile fileformData.append("uploadFile", file);// 無需點擊確定,直接發送請求,上傳文件到數據庫,實現 SQL Server image 類型文件上傳await qualityFileUploadFileWithPutService(qualityFileObj.value.fileNo, formData);} else {// 將文件對象 file 添加到 formData 對象中,uploadFile 需要與后端接口中接收文件的參數名一致,如果不一致,則后端需要指定參數名,如 @RequestPart("uploadFile") MultipartFile fileformData.append("uploadFile", file);// 將普通對象 qualityFileObj 的 fileNo 屬性添加到 formData 對象中formData.append("fileNo", qualityFileObj.value.fileNo);// 無需點擊確定,直接發送請求,上傳文件到數據庫,實現 SQL Server image 類型文件上傳await qualityFileUploadFileService(formData);}// 點擊【上傳/重傳】選擇文件后,上傳文件完成,通知父組件更新文件路徑名稱和是否空內容的操作emit("upload-file-complete", file.name);// 同步更新表單數據qualityFileObj.value.filePathname = file.name;qualityFileObj.value.isNullContent = false;}
};
......
</script><template>
......<el-table-column label="操作" width="150" header-align="center" align="center" fixed="right"><template #default="scope"><BasePreventReClickButton type="primary" plain :loading="false" @click="onUploadClick">{{qualityFileObj.isNullContent ? "上傳" : "重傳"}}</BasePreventReClickButton>></template></el-table-column>
......
</template>
qualityFile.ts
import request from "@/utils/request";
import type { IQualityFile, IQualityFileQueryObj } from "@/views/resources/QualityFile/types";/*** 上傳質量體系文件,實現 SQL Server image 類型文件上傳,使用 put 發送請求,發送的數據有:請求體數據(文件數據 uploadFile),請求參數數據(文件編號 fileNo)* @param fileNo 文件編號(可能包含特殊字符如 /)* @param formData 表單數據,包含的數據只有:文件數據(uploadFile)* @returns*/
export const qualityFileUploadFileWithPutService = (fileNo: string, formData: FormData) => {// 發送請求,發送的數據有:請求體數據(文件數據 uploadFile),請求參數數據(文件編號 fileNo)return request.put("/resources/qualityFile/uploadFile", formData, {params: {fileNo: fileNo},// 上傳文件,需設置 headers 信息,將"Content-Type"設置為"multipart/form-data"headers: {"Content-Type": "multipart/form-data"}});
};/*** 上傳質量體系文件,實現 SQL Server image 類型文件上傳,使用 post 發送請求,發送的數據只有:請求體數據(文件數據 uploadFile)* @param formData 表單數據,包含的數據有:文件數據(uploadFile)和 文件編號(fileNo) {@link FormData}* @returns*/
export const qualityFileUploadFileService = (formData: FormData) => {return request.post("/resources/qualityFile/uploadFile", formData, {// 上傳文件,需設置 headers 信息,將"Content-Type"設置為"multipart/form-data"headers: {"Content-Type": "multipart/form-data"}});
};
fileUtils.ts
/*** 獲取文件名的擴展名(后綴)* @param filename 文件名* @returns 擴展名(后綴)*/
export const getExtension = (filename: string) => {// 方法1:?使用split()和pop(),通過將文件名按點號(.)分割成數組,取最后一個元素作為后綴名。// const parts = filename.split(".");// return parts.length > 1 ? parts.pop() : "";// 方法2:?使用lastIndexOf()和substring(),更健壯的方式是定位最后一個點號的位置后截取字符串,避免多重點號的誤判// const lastDotIndex = filename.lastIndexOf(".");// return lastDotIndex !== -1 ? filename.substring(lastDotIndex + 1) : "";// 方法3:使用slice()和lastIndexOf()// const lastDotIndex = filename.lastIndexOf(".");// return lastDotIndex !== -1 ? filename.slice(lastDotIndex + 1) : "";// 方法4:使用正則表達式const match = filename.match(/\.([a-zA-Z0-9]+)$/);return match ? match[1] : "";
};/*** 獲取URL中的文件名的擴展名(后綴)* @param url url* @returns 擴展名(后綴)*/
export const getExtensionFromURL = (url: string) => {const pathname = new URL(url).pathname;return getExtension(pathname);
};
2、后端 Spring boot + Mybatis
控制層:QualityFileController.java
package com.weiyu.controller;import com.alibaba.fastjson.JSON;
import com.weiyu.anno.Debounce;
import com.weiyu.pojo.QualityFile;
import com.weiyu.pojo.QualityFileDTO;
import com.weiyu.pojo.QualityFileQueryDTO;
import com.weiyu.pojo.Result;
import com.weiyu.service.QualityFileService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.List;/*** 質量體系文件 Controller*/
@RestController
@RequestMapping("/resources/qualityFile")
@Slf4j
public class QualityFileController {@Autowiredprivate QualityFileService qualityFileService;/*** 質量體系文件上傳,實現 SQL Server image 類型文件上傳,使用 @PutMapping 接收請求* MultipartFile參數名稱說明:* 因為前端使用 formData.append("uploadFile", file) 用的參數名稱是 uploadFile* 后端這里使用 uploadFile 與前端一致,可以不使用 @RequestPart("uploadFile"),也可以使用* @param fileNo 文件編號(可能包含特殊字符如 /)* @param uploadFile 上傳文件 {@link MultipartFile}* @apiNote 本接口使用防抖機制,3s 內重復請求會被忽略*/@PutMapping("/uploadFile")@Debounce(key = "/resources/qualityFile/uploadFile", value = 3000)public Result<?> uploadFile(@RequestParam String fileNo, MultipartFile uploadFile) {try {log.info("【質量體系文件】,上傳,實現 SQL Server image 類型文件上傳,使用 @PutMapping 接收請求," +"/resources/qualityFile/uploadFile,fileNo = {},uploadFile = {}", fileNo, uploadFile);qualityFileService.uploadFile(fileNo, uploadFile);return Result.success("文件上傳成功!");} catch (Exception e) {return Result.success("文件上傳失敗:" + e.getMessage());}}/*** 質量體系文件上傳,實現 SQL Server image 類型文件上傳,使用 @PostMapping 接收請求* MultipartFile參數名稱說明:* 因為前端使用 formData.append("uploadFile", file) 用的參數名稱是 uploadFile* 后端這里使用 uploadFile 與前端一致,可以不使用 @RequestPart("uploadFile"),也可以使用* String參數名稱說明:* 因為前端使用 formData.append("fileNo", qualityFileObj.value.fileNo) 用的參數名稱是 fileNo* 后端這里使用 fileNo 與前端一致,可以不使用 @RequestPart("fileNo"),也可以使用* @param fileNo 文件編號(可能包含特殊字符如 /)* @param uploadFile 上傳文件 {@link MultipartFile}* @apiNote 本接口使用防抖機制,3s 內重復請求會被忽略*/@PostMapping("/uploadFile")@Debounce(key = "/resources/qualityFile/uploadFile", value = 3000)public Result<?> uploadFile(MultipartFile uploadFile, String fileNo) {try {log.info("【質量體系文件】,上傳,實現 SQL Server image 類型文件上傳,使用 @PostMapping 接收請求," +"/resources/qualityFile/uploadFile,uploadFile = {},fileNo = {}", uploadFile, fileNo);qualityFileService.uploadFile(fileNo, uploadFile);return Result.success("文件上傳成功!");} catch (Exception e) {return Result.success("文件上傳失敗:" + e.getMessage());}}
}
服務層接口實現:QualityFileServiceImpl?.java
package com.weiyu.service.impl;import com.weiyu.mapper.QualityFileMapper;
import com.weiyu.pojo.FileData;
import com.weiyu.pojo.QualityFile;
import com.weiyu.pojo.QualityFileQueryDTO;
import com.weiyu.service.QualityFileService;
import com.weiyu.utils.FileDownloadUtil;
import jakarta.validation.constraints.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.Resource;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.ArrayList;
import java.util.List;/*** 質量體系文件 Service 接口實現*/
@Service
public class QualityFileServiceImpl implements QualityFileService {@Autowiredprivate QualityFileMapper qualityFileMapper;/*** 上傳質量體系文件** @param fileNo 文件編號* @param uploadFile 上傳文件*/@Overridepublic void uploadFile(String fileNo, MultipartFile uploadFile) throws IOException {FileData fileData = new FileData();fileData.setFileName(uploadFile.getOriginalFilename());fileData.setFileContent(uploadFile.getBytes());// todo: 如果是大文件(超過10MB)保存到文件系統,數據庫只保存文件路徑;否則保存到數據庫// 保存文件到數據庫qualityFileMapper.saveFile(fileNo, fileData);}
}
數據表結構數據傳輸對象 DTO:FileData.java
package com.weiyu.pojo;import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;/*** 文件數據*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class FileData {private String fileName;private byte[] fileContent;
}
持久層:QualityFileMapper.java
package com.weiyu.mapper;import com.weiyu.pojo.FileData;
import com.weiyu.pojo.QualityFile;
import com.weiyu.pojo.QualityFileQueryDTO;
import org.apache.ibatis.annotations.Mapper;import java.util.List;/*** 質量體系文件 Mapper*/
@Mapper
public interface QualityFileMapper {/*** 保存質量體系文件數據到數據庫* @param fileNo 文件編號* @param fileData 上傳文件*/void saveFile(String fileNo, FileData fileData);
}
持久層數據庫sql更新:QualityFileMapper.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.weiyu.mapper.QualityFileMapper"><!--mssql--><!-- 保存質量體系文件數據到數據庫 --><update id="saveFile">update ControledFileMain setcfm_ContentFileName = #{fileData.fileName}, cfm_Content = #{fileData.fileContent}, cfm_ContentIsNull = 0where Cfm_BigType = '3' and Cfm_ID = #{fileNo}</update>
</mapper>
3、應用效果
文件名稱支持空格和加號