SpringBoot+Vue 2 多方法实现(图片/视频/报表)文件上传下载,示例超详细 !

目录

一、主流方法介绍

1. Base 64

2. 二进制流传输

3. multipart/form-data

4. FTP/SFTP

5. 云存储服务API

二、multipart/form-data 方式上传单个文件

1、前端部分

2、后端部分

三、multipart/form-data 方式上传多个文件

1、前端部分

 2、后端部分

四、Base 64 方式上传单个或多个文件

1、Base64 和 二进制流 是两种不同的概念:

2、前端部分

3、后端部分

五、二进制流传输文件

1、前端部分

2、后端部分


一、主流方法介绍

1. Base 64

优点:简单易用,可以直接嵌入到文本中。适用于小文件或需要将文件嵌入到JSON等文本格式中的场景。
缺点:编码后的数据大小会增加约33%,不适合大文件传输。需要额外的编码和解码步骤,增加了处理时间。

2. 二进制流传输

优点:传输效率高,适合大文件传输。不需要额外的编码和解码步骤。
缺点:实现相对复杂,需要处理二进制数据。不适合直接嵌入到文本协议中。

3. multipart/form-data

优点:通过HTTP POST请求中的multipart/form-data格式上传文件,易于实现。支持同时上传多个文件和文本字段。
缺点:性能略低于二进制流传输。处理大文件时可能需要更多的内存和处理时间。

4. FTP/SFTP

优点:适合需要长期稳定传输大量文件的场景。提供了丰富的文件管理功能,如目录操作、权限管理等。
缺点:需要额外的服务器配置和维护。安全性相对较低(FTP),SFTP更安全但配置复杂。

5. 云存储服务API

优点:高性能和高可用性,通常具有CDN加速功能。提供丰富的API和SDK,易于集成。安全性高,支持多种认证机制。(如阿里云OSS、AWS S3等)
缺点:需要支付云服务费用。可能存在网络延迟问题。

下面将主要对前三种方式进行演示说明,并附上前后端的示例代码。

二、multipart/form-data 方式上传单个文件

 

1、前端部分

<template><div class="uploadFile"><section><div class="selectFileBox"><el-uploadref="uploadExcel"action="/sensorjxpro/eutest/excel":limit="limitNum"name="file":auto-upload="true"accept=".xlsx":before-upload="beforeUploadFile":before-remove='removeFile':on-exceed="exceedFile":on-error="handleError":file-list="fileList":http-request="httpRequest"><div class="img"><img src="@/assets/upload.png" alt=""></div><el-button size="small" type="primary">选择文件</el-button><div slot="tip" class="el-upload__tip">只能上传xlsx文件,且不超过10M</div></el-upload><br/><el-button  size="small" type="primary" @click="goReview" :disabled="isCanClick==true ? false : true">预览文件</el-button></div></section></div>
</template><script>import {UploadFile} from '@/api/request'import domMessage from '@/utils/messageOnce'const messageOnce = new domMessage()
export default {data(){return {limitNum:1,fileList:[],isCanClick:false}},methods:{goReview(){this.$router.push('/sensorjxpro/web/operationfile/review')this.$emit('setActive', 1);},removeFile(){this.fileList=[]this.isCanClick = false},// 上传文件之前的钩子, 参数为上传的文件,若返回 false 或者返回 Promise 且被 reject,则停止上传beforeUploadFile(file) {const extension = file.name.substring(file.name.lastIndexOf('.') + 1)const size = file.size / 1024 / 1024if (extension !== 'xlsx' || file.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet') {messageOnce.error({message:'请上传后缀为.xlsx的excel文件',type:'warning'})this.isCanClick = false}if (size > 10) {messageOnce.error({message:'文件大小不得超过10M',type:'warning'})this.isCanClick = false}},// 文件超出个数限制时的钩子exceedFile(files, fileList) {messageOnce.error({type:'warning',message: `只能选择 ${this.limitNum} 个文件,当前共选择了 ${files.length + fileList.length} 个`})this.isCanClick = false},// 文件上传失败时的钩子handleError(err) {this.$message.error(err.msg)this.isCanClick = false},httpRequest(item){console.log('item',item);this.fileList = item.filethis.importData()},importData(){let formData = new FormData() //创建一个 FormData 对象formData.append('file', this.fileList) // 将文件添加到 FormData 对象中UploadFile(formData).then((res)=>{ // 调用 UploadFile API 上传文件,并处理响应。const resData = JSON.parse(res.data)if(resData.result == 1){this.$toast('上传成功')// 将表头和表格数据存起来sessionStorage.setItem('SET_EXCELHEADER',JSON.stringify(resData.data.excelHeaders))sessionStorage.setItem('SET_EXCELDATA',JSON.stringify(resData.data.inJxContents))// this.$store.commit('SET_EXCELHEADER',resData.data.excelHeaders)// this.$store.commit('SET_EXCELDATA',resData.data.inJxContents)this.isCanClick = true // 启用预览按钮}console.log('上传文件res',res);})}}}
</script><style lang="scss" scoped>@import '@/style/uploadFile/uploadFile.scss'
</style>

对于上传的组件,使用的是element ui  组件 | Element

          <el-uploadref="uploadExcel"action="/sensorjxpro/eutest/excel":limit="limitNum"name="file":auto-upload="true"accept=".xlsx":before-upload="beforeUploadFile":before-remove='removeFile':on-exceed="exceedFile":on-error="handleError":file-list="fileList":http-request="httpRequest">
****************************************************************
el-upload 是 Element UI 提供的文件上传组件。
ref="uploadExcel":给组件设置一个引用名称,方便在 JavaScript 中操作。
action="/sensorjxpro/eutest/excel":文件上传的 URL。
:limit="limitNum":限制上传文件的数量,limitNum 是一个绑定的数据属性。
name="file":上传文件的表单字段名。
:auto-upload="true":是否自动上传文件。
accept=".xlsx":只允许上传 .xlsx 文件。
:before-upload="beforeUploadFile":文件上传前的钩子函数。
:before-remove="removeFile":文件移除前的钩子函数。
:on-exceed="exceedFile":超过文件数量限制时的钩子函数。
:on-error="handleError":文件上传失败时的钩子函数。
:file-list="fileList":已上传文件列表,fileList 是一个绑定的数据属性。
:http-request="httpRequest":自定义上传请求的函数。

2、后端部分

使用 MultipartFile 对象进行接收,以及后续的逻辑操作

    @PostMapping("/upload")public RespondDto uploadFile(@RequestParam("file") MultipartFile multipartFile) {ExcelOperatVo excelOperatVo = excelOperatService.uploadExcel(multipartFile);return new RespondDto(excelOperatVo);}
    public ExcelOperatVo uploadExcel(MultipartFile multipartFile) {//       保存文件到本地File dir = new File("uploadFile/excel");if (!dir.exists()) {dir.mkdirs();}LocalDateTime current = LocalDateTime.now();DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");String formatted = current.format(formatter);//加上三位随机数Random random = new Random();int end3 = random.nextInt(999);File file = new File(dir.getAbsolutePath() + File.separator + formatted + "-" + end3 + "-" + multipartFile.getOriginalFilename());try {multipartFile.transferTo(file);} catch (IOException e) {e.printStackTrace();}log.info("【上传文件》文件已保存到本地:{}】",file.getAbsolutePath());
//        获取excel文件内容ArrayList<InJxContent> inJxContents = readExcel(file.getAbsolutePath());//        读取excel表头 新建动态数组,返回两个数组SSExcel07Sheet sheet = new SSExcel07Workbook().open(file.getAbsolutePath()).getSheet(0);ArrayList<ExcelHeaderTvo> excelHeaderTvos = new ArrayList<>();for (int i = 0; i < 7; i++) {String cellValue = sheet.getCell(0, i).getCellValue().toString();ExcelHeaderTvo excelHeaderTvo = new ExcelHeaderTvo(cellValue, cellValue, cellValue, "center", "13%", true);if(i <= 1){excelHeaderTvo.setEdit(false);}switch (i) {case 0:excelHeaderTvo.setField("tel");break;case 1:excelHeaderTvo.setField("name");break;case 2:excelHeaderTvo.setField("degree");break;case 3:excelHeaderTvo.setField("attitude");break;case 4:excelHeaderTvo.setField("duty");break;case 5:excelHeaderTvo.setField("pMPoints");break;case 6:excelHeaderTvo.setField("jxPoints");break;case 7:excelHeaderTvo.setField("coefficient");break;}excelHeaderTvos.add(excelHeaderTvo);}ExcelOperatVo excelOperatVo = new ExcelOperatVo(excelHeaderTvos, inJxContents);return excelOperatVo;}
 private ArrayList<InJxContent> readExcel(String filePath) {SSExcel07Sheet sheet = (new SSExcel07Workbook()).open(filePath).getSheet(0);ArrayList<InJxContent> inJxContents = new ArrayList<>();log.info("【readExcel方法开始:】");//外层控制行,每行为一个jxContent对象for (int i = sheet.getFirstRowNum() + 1; i <= sheet.getLastRowNum(); i++) {InJxContent injxContent = new InJxContent();//内层各项为各列属性值for (int j = 0; j <= 7; j++) {if (sheet.getCell(i, j).getCellValue() != null || j == 2) {switch (j) {case 0:injxContent.setTel(BigInteger.valueOf(Long.valueOf((String) sheet.getCell(i, j).getCellValue())));break;case 1:injxContent.setName((String) sheet.getCell(i, j).getCellValue());break;case 2:Double d1 = sheet.getCell(i, j).getXSSFCell().getNumericCellValue() * 100;injxContent.setDegree(new DecimalFormat("#").format(d1) + "%");break;case 3:injxContent.setAttitude(Integer.valueOf((String) sheet.getCell(i, j).getCellValue()));break;case 4:injxContent.setDuty(Integer.valueOf((String) sheet.getCell(i, j).getCellValue()));break;case 5:injxContent.setPmPoints((String) sheet.getCell(i, j).getCellValue());break;case 6:Float v = Float.parseFloat((String) sheet.getCell(i, j).getCellValue());String format = new DecimalFormat("#.#").format(v);injxContent.setJxPoints(Float.parseFloat(format));break;case 7:injxContent.setCoefficient(Float.parseFloat((String) sheet.getCell(i, j).getCellValue()));break;default:log.info("执行了default");break;}}}injxContent.setRowkey(i);inJxContents.add(injxContent);}log.info("【上传文件:】excel:内容:{}",inJxContents);return inJxContents;}

三、multipart/form-data 方式上传多个文件

        单文件和多文件上传没用太大的区别,但是还是分开介绍一下,此处支持选择一个或多个文件文件后,点击上传一次性全部上传完

 这段代码跟单个文件上传相比,主要是加入了循环遍历的逻辑:

      // 获取文件输入框的引用const fileInputs = this.$refs.uploadForm.querySelectorAll('input[type="file"]');let hasFileSelected = false;fileInputs.forEach((fileInput) => {const fileList = fileInput.files;for (let i = 0; i < fileList.length; i++) {formData.append('multipartFiles', fileList[i]);formData.append('types', fileInput.id);hasFileSelected = true;}});

1、前端部分

<template><div><h1>文件上传和导出</h1><form ref="uploadForm" @submit="uploadFormSubmit"  enctype="multipart/form-data"><div><label for="初测主特性">初测主特性文件:</label><input type="file" id="初测主特性" name="files[]" accept=".xlsx"><el-button class="export-file" >从数据库中导出</el-button></div>。。。<div><label for="指标要求">指标要求文件:</label><input type="file" id="指标要求" name="files[]" accept=".xlsx"><el-button class="export-file" >从数据库中导出</el-button></div><el-button type="primary" @click="uploadFormSubmit">上传</el-button><el-button :disabled="!fileUrls" @click="exportButtonClick">导出测试报告</el-button></form></div>
</template>
<script>
export default {data() {return {fileUrls: null,};},methods: {uploadFormSubmit(e) {e.preventDefault();const formData = new FormData();// 获取文件输入框的引用const fileInputs = this.$refs.uploadForm.querySelectorAll('input[type="file"]');let hasFileSelected = false;fileInputs.forEach((fileInput) => {const fileList = fileInput.files;for (let i = 0; i < fileList.length; i++) {formData.append('multipartFiles', fileList[i]);formData.append('types', fileInput.id);hasFileSelected = true;}});if (!hasFileSelected) {alert('请至少选择一个文件!');return;}this.showLoadingMessage('文件上传中...');this.$request.post('/manage/server/uploadExcels', formData).then((response) => {// console.log(response.data);const data = response.data;if (data.result === 1 && data.data.length > 0) {this.fileUrls = data.data;this.showMessage('文件上传完成!', 'success');}}).catch((error) => {console.error('上传失败:', error);this.showMessage('文件上传失败,请重试!', 'error');});},exportButtonClick() {const fileUrls = this.fileUrls;console.log(fileUrls);if (!fileUrls) {alert('请先上传文件!');return;}this.showLoadingMessage('文件导出中...');this.$request.get('/manage/server/writeExcel', {params: {// fileUrls: fileUrls,// 通过将参数值转换为逗号分隔的字符串,可以避免方括号被自动编码,从而解决这个异常问题。记得在后端接收参数时进行相应的处理,将逗号分隔的字符串转换为数组再进行后续操作。fileUrls: fileUrls.join(',')},responseType: 'blob'}).then((response) => {const blob = new Blob([response.data]);const url = window.URL.createObjectURL(blob);const a = document.createElement('a');a.href = url;a.download = '测试报告.xlsx';a.click();this.showMessage('文件导出完成!', 'success');setTimeout(() => {const fileInputs = this.$refs.uploadForm.querySelectorAll('input[type="file"]');fileInputs.forEach((fileInput) => {fileInput.value = '';});this.fileUrls = null;const messageElements = document.querySelectorAll('.loading, .success, .error');messageElements.forEach((messageElement) => {messageElement.remove();});}, 5000)}).catch((error) => {console.error('导出失败:', error);this.showMessage('文件导出失败,请重试!', 'error');});},showLoadingMessage(message) {const loadingElement = document.createElement('div');loadingElement.classList.add('loading');loadingElement.innerText = message;this.$refs.uploadForm.appendChild(loadingElement);},showMessage(message, type) {const messageElement = document.createElement('div');messageElement.classList.add(type);messageElement.innerText = message;this.$refs.uploadForm.appendChild(messageElement);}}
}
</script>

 2、后端部分

后端接收参数则从 MultipartFile 变成了 MultipartFile[] 

    /*** 1、接受前端传入的文件数组和文件类型对应的数组【文件和类型数组要一一对应】,下载到本地,并将下载到本地的文件路径返回给前端* 2、url: http://localhost:xxxxx/manage/server/uploadExcels* @param multipartFiles* @param types*/@PostMapping(value = "/uploadEndTestExcels")public RespondDto upLoadFiles(@NonNull @RequestParam("multipartFiles") MultipartFile[] multipartFiles,@NonNull @RequestParam("types") String[] types) throws IOException {return fileService.upLoadFiles(multipartFiles,types);}/*** 1、接受前端传入的文件路径参数,对文件进行解析,最终生成/导出一个最终的Excel文件* 2、url: http://localhost:xxxx/manage/server/writeExcel* @param fileUrls* @throws IOException* @throws URISyntaxException*/@GetMapping(value = "/writeEndTestExcel")public void writeExcel(HttpServletResponse response ,@RequestParam("fileUrls") String[] fileUrls) throws IOException, URISyntaxException {fileService.writeExcel(response,fileUrls);}
    /*** 实现多文件一次性上传功能【上传多个Excel文件,并获取每个上传文件的类型】** 0、实现上传的文件保存到本地* 1、将每个文件的名字以文件类型的名字进行重命名,并且保存到指定文件路径下,并且将文件文件路径以数组的形式返回给前端*/public RespondDto upLoadFiles(MultipartFile[] files, String[] types) throws IOException {String formattedDate = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd"));//加上三位随机数int randomNum = new Random().nextInt(999);//将每次上传的Excel文件放到指定的文件夹下File dir = new File(absolutePath + File.separator + "uploadFile" + File.separator + formattedDate + randomNum);if (!dir.exists()) {dir.mkdirs();}// 将重命名文件名变量fileRename移至循环外部,初始化为nullString fileRename = null;// 将fileToSave移至循环外部,初始化为nullFile fileToSave;List<String> fileUrls = new ArrayList<String>();// 存储已上传的type类型Set<String> uploadedTypes = new HashSet<String>();for (int i = 0; i < files.length; i++) {MultipartFile file = files[i];String type = types[i];// 判断文件或类型不能为空,不能上传空文件if (file.isEmpty() || StringUtils.isEmpty(type)) {return new RespondDto<>("文件或类型不能为空,请重新上传!");}String originalFileName = file.getOriginalFilename();// 判断文件后缀名是否符合要求if (!originalFileName.endsWith("xlsx")) {log.error(originalFileName + "不是.xlsx后缀的Excel文件!");throw new RuntimeException("仅支持.xlsx后缀的Excel文件!");}uploadedTypes.add(type);String fileExt = originalFileName.substring(originalFileName.lastIndexOf("."));Workbook workbook = new XSSFWorkbook(file.getInputStream());Sheet sheet = workbook.getSheet("XXXX表");if (Objects.isNull(sheet)) {Sheet sheet2 = workbook.getSheet("增益压缩");Row row = sheet2.getRow(0);Cell cell = row.getCell(0);if (cell != null && Objects.equals(cell.getCellTypeEnum(), CellType.STRING)) {String value = cell.getStringCellValue();if (value.equals("测试项目")) {fileRename = type + "指标";}}} else {Row row = sheet.getRow(0);Cell cell = row.getCell(7);if (cell != null && Objects.equals(cell.getCellTypeEnum(), CellType.STRING)) {String value = cell.getStringCellValue();fileRename = type + "XX性" + value.substring(value.length() - 3);}}// 在循环内部为fileToSave赋值fileToSave = new File(dir.getAbsolutePath() + File.separator + fileRename + fileExt);if (fileToSave.exists()) {fileToSave.delete();}FileOutputStream os = null;try {os = new FileOutputStream(fileToSave);IOUtils.copy(file.getInputStream(), os);String fileUrl = fileToSave.getAbsolutePath();fileUrls.add(fileUrl);} catch (Exception e) {e.printStackTrace();return new RespondDto<>("文件上传失败!");} finally {if (os != null) {IOUtils.closeQuietly(os);}}log.info("【源文件】" + originalFileName + "已保存到本地,【文件名】是 " + fileRename + " 【存放路径是】 " + fileToSave.getAbsolutePath());}return new RespondDto<>(fileUrls);}
    /*** 实现多文件解析并导出生成测试报告功能** 0、将源文件路径遍历出来,每次都放到输入流中,同时去遍历配置文件,将类型名称与文件名称符合的配置文件读取出来* 1、进行copy操作,最后将源文件路径遍历完后将目标Excel进行保存*/@Overridepublic RespondDto writeExcel(HttpServletResponse response, String[] fileUrls) throws IOException, URISyntaxException {String excelFilePath = absolutePath + File.separator + "template.xlsx";FileInputStream destinationFileInputStream = new FileInputStream(excelFilePath);Workbook destinationWorkbook = new XSSFWorkbook(destinationFileInputStream);List<String> errorMessages = new ArrayList<>();Integer currentRow = 0;for (String url : fileUrls) {// 追踪当前需要写入的行数processFile(url, destinationWorkbook, configCheck.getConfig(), errorMessages , currentRow);if(url != null && url.contains("符合性")){currentRow++;}}destinationWorkbook.setForceFormulaRecalculation(true);destinationWorkbook.write(response.getOutputStream());destinationFileInputStream.close();destinationWorkbook.close();if (errorMessages.isEmpty()) {return new RespondDto<>("Excel导出成功!");} else {String errorInfo = String.join("\n", errorMessages);return new RespondDto<>("Excel导出过程中发生错误:\n" + errorInfo);}}

四、Base 64 方式上传单个或多个文件

1、Base64 和 二进制流 是两种不同的概念:

二进制流:
直接传输文件的原始字节数据。适用于大文件传输,效率高。不需要额外的编码和解码步骤。
Base64:
将二进制数据转换为文本形式,以便在文本协议(如HTTP)中传输。编码后的数据大小会增加约33%。需要在发送前进行编码,在接收后进行解码。

2、前端部分

        <el-col :span="24"><el-form-item label="上传文件:"><el-button type="primary" @click="handleFileSelect">拍照/视频</el-button><input id="fileInput" type="file" ref="fileInput" multiple accept="image/*,video/*"@change="handleFileChange" style="display: none;"><div v-for="(file, index) in form.fileList" :key="index"><el-image :src="file.content" v-if="file.type === 'image'"                style="max-width: 100px; max-height: 100px;"></el-image><video v-else-if="file.type === 'video'" :src="file.content" controls                style="max-width: 100px; max-height: 100px;"></video><i class="el-icon-delete" @click="deleteFile(index)"></i></div><div style="display: block; font-size: 12px; color: #888;">支持一次上传一个/多个文件,但不是必选项,若无法描述清楚,或需操作多步骤时应拍照/视频</div></el-form-item></el-col>
    handleFileSelect() {console.log('文件选择操作触发');this.$refs.fileInput.click();},handleFileChange(event) {console.log('文件改变事件触发');let files = event.target.files;for (let i = 0; i < files.length; i++) {const file = files[i];// 检查文件类型是否符合要求if (!this.checkFileType(file)) {alert('文件格式不符合要求,图片只能识别png、jpg、jpeg,视频只能识别mp4、avi、mov !');continue;}const reader = new FileReader();reader.onload = e => {let fileType = file.type.includes('video') ? 'video' : 'image';this.form.fileList.push({ content: e.target.result, type: fileType, file: file });};reader.readAsDataURL(file);}},checkFileType(file) {const imageTypes = ['image/png', 'image/jpeg', 'image/jpg'];const videoTypes = ['video/mp4', 'video/avi', 'video/quicktime'];if (file.type.includes('image')) {return imageTypes.includes(file.type);} else if (file.type.includes('video')) {return videoTypes.includes(file.type);}return false;},toBase64(file, callback) {console.log('开始转换文件为Base64编码');const reader = new FileReader();reader.readAsDataURL(file);reader.onload = () => {const base64 = reader.result.split(',')[1];callback(base64);};reader.onerror = error => {console.error('Error converting file to base64:', error);};},saveDataToBackend() {console.log('开始保存数据到后端');if (!this.form.operation) {alert('请完成备注后再保存!');return;}this.loading = true; // 开始保存时显示loading图标const reqData = {productId: this.form.productId,currentLocation: this.form.currentLocation,operation: this.form.operation, // 添加操作种类fileList: this.form.fileList.length === 0 ? null : [], // 根据文件列表是否为空初始化 fileListuserIndex: this.getPersonIndex(),questReason: this.form.questReason, // 问题描述isUsed: sessionStorage.getItem(key_DingResponseUsed) || cachedResponseUsed ,isStored: sessionStorage.getItem(key_DingResponseStored) || cachedResponseStored,belongContent:this.form.belongContent //归属项目};// 如果 fileList 不为空,进行 Base64 转换if (this.form.fileList.length > 0) {const promises = this.form.fileList.map(file => {return new Promise((resolve) => {this.toBase64(file.file, base64 => {reqData.fileList.push({content: base64,type: file.type,});resolve();});});});// 等待所有文件转换完成后再发送请求Promise.all(promises).then(() => {// 发送请求到后端return SensorBorderRequest.SaveAllCardRecords(reqData);}).then(response => {console.log('保存成功:', response);this.$message.success('保存成功!');setTimeout(() => {this.resetTestingFields(); // 重置表单字段this.$router.push("/ddinguia/web/history");}, 500); // 0.5 秒后跳转}).catch(error => {console.error('保存失败:', error);this.$message.error('保存失败!');}).finally(() => {this.loading = false; // 保存完成后取消loading状态});} else {// 如果 fileList 为空,直接发送请求SensorBorderRequest.SaveAllCardRecords(reqData).then(response => {console.log('保存成功:', response);this.$message.success('保存成功!');setTimeout(() => {this.resetTestingFields(); // 重置表单字段this.$router.push("/ddinguia/web/history");}, 500); // 0.5 秒后跳转}).catch(error => {console.error('保存失败:', error);this.$message.error('保存失败!');}).finally(() => {this.loading = false; // 保存完成后取消loading状态});}},

3、后端部分

对于后端部分,接收到的文件就是以base64编码的字符串,按照对象格式进行接收转化,再解码就可以保存到服务器上了

    public class ContentFile {private String name;//文件名private String content;// base64private String type; // 图片或者视频}private List<String> saveFilesByType(List<ContentFile> files) {if (files == null || files.isEmpty()) {return new ArrayList<>();}List<String> urlList = new ArrayList<>();for (ContentFile file : files) {if (file == null || file.getContent() == null || file.getContent().isEmpty()) {continue;}String fileType = file.getType();String extension = getFileExtensionByType(fileType);if (extension.isEmpty()) {// 不支持的文件类型,进行相应的处理continue;}String fileName;if (file.getName() != null && !file.getName().isEmpty()) {// 获取UUID,并截取前三位
//                String shortUUID = UUID.randomUUID().toString().substring(0, 3);//fileName = file.getName() + shortUUID + extension;fileName = file.getName() +  extension;} else {
//                System.out.println("44444444444");// 获取当前时间Date currentDate = new Date();// 格式化日期时间SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd_HHmmss");String formattedDate = dateFormat.format(currentDate);// 获取UUID,并截取前三位String shortUUID = UUID.randomUUID().toString().substring(0, 3);// 构造新的文件名fileName = fileType + "_" + formattedDate + "_" + shortUUID + extension;}File directory = new File(fileProperties.getSavePath() + File.separator + fileType + "s");System.out.println("fileProperties.getSavePath()对应的路径是:" + directory);if (!directory.exists()) {directory.mkdirs();}String fileSavePath = fileProperties.getSavePath() + fileType + "s" + File.separator + fileName;System.out.println("fileSavePath文件保存的路径是:" + fileSavePath);try (FileOutputStream fos = new FileOutputStream(fileSavePath)) {byte[] decodedBytes = Base64.getDecoder().decode(file.getContent());fos.write(decodedBytes);urlList.add("static/ddinguia" + File.separator + "server" + File.separator + "files" + File.separator + fileType + "s" + File.separator + fileName);System.out.println(urlList);} catch (IOException e) {throw new RuntimeException(e);}}return urlList;}

关于base 64的工具类 可以自己写,也可以使用 java.util 自带的

五、二进制流传输文件

1、前端部分

        使用FormData对象可以方便地处理文件上传,但这里为了演示二进制流传输,直接将文件读取为二进制数据

<template><div><input type="file" @change="handleFileChange" /><button @click="uploadFile">上传</button></div>
</template><script>
import axios from 'axios';export default {data() {return {file: null,};},methods: {handleFileChange(event) {this.file = event.target.files[0];},async uploadFile() {if (!this.file) {alert('请选择一个文件');return;}const formData = new FormData();formData.append('file', this.file);try {const response = await axios.post('http://localhost:8080/upload', formData, {headers: {'Content-Type': 'application/octet-stream',},});console.log('文件上传成功', response.data);} catch (error) {console.error('文件上传失败', error);}},},
};
</script>

2、后端部分

        Spring Boot 使用@RequestBody注解接收二进制数据,并将其保存到指定路径。

import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;@RestController
public class FileUploadController {@PostMapping("/upload")public String uploadFile(@RequestBody byte[] fileData) {try {// 保存文件到指定路径Path path = Path.of("path/to/save/file");Files.write(path, fileData, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING);return "文件上传成功";} catch (IOException e) {e.printStackTrace();return "文件上传失败";}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/61314.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据库迁移--laravel进阶篇

本地开发中的数据库和线上发布的数据库是不一样的,每进行一个线上版本的更新,很可能也涉及大量数据库的改动,那么这些数据库的改动在laravel中可以使用数据库迁移来处理。 比如我想创建一张flights数据表 执行php artisan make:migration create_flights_table命令就能自动生…

uni-app 修改复选框checkbox选中后背景和字体颜色

编写css&#xff08;注意&#xff1a;这个样式必须写在App.vue里&#xff09; /* 复选框 */ /* 复选框-圆角 */ checkbox.checkbox-round .wx-checkbox-input, checkbox.checkbox-round .uni-checkbox-input {border-radius: 100rpx; } /* 复选框-背景颜色 */ checkbox.checkb…

java实现ModbusCRC16校验

1. 目的 电流数据采集器是RS485 Modbus协议&#xff0c;想通过java实现串口数据的采集&#xff0c;因为涉及到串口通信的CRC校验&#xff0c;所以需要通过java来实现对采集的电流值进行校验&#xff0c;确保每次读到的电流值都是正常的。 2. 什么是ModbusCRC16校验 Modbus协议…

aws上安装ssm-agent

aws-cloudwatch 连接机器 下载ssm-agent aws-ec2 安装ssm-agent aws-linux安装ssm-agent 使用 SSM 代理查找 AMI 预装 先运行&#xff1a;systemctl status amazon-ssm-agent 查看sshm-agent的状态。 然后安装提示&#xff0c;执行 systemctl start amazon-ssm-agent 启动即…

springboot+vue+SseEmitter数据流推送实战

业务场景 SseEmitter介绍 SseEmitter 是 Spring Boot 中用于实现服务器发送事件&#xff08;Server-Sent Events, SSE&#xff09;的一种机制。SSE 允许服务器向客户端推送实时数据&#xff0c;而不需要客户端频繁地发起请求。这对于实现实时通知、更新等场景非常有用。 Sse…

阿里云 DevOps 资源安全扫描实践

随着企业上云进程的加速&#xff0c;云资源的使用量日益增长&#xff0c;云环境中资源的安全性和稳定性成为了企业业务运营的关键要素 面对多样化的云资源和复杂的应用场景&#xff0c;传统的安全管理手段已无法完全满足企业日益严苛的安全需求。为了确保云上资源的安全性&…

python之sklearn--鸢尾花数据集之数据降维(PCA主成分分析)

python之sklearn–鸢尾花数据集之数据降维(PCA主成分分析) sklearn库&#xff1a;Scikit - learn&#xff08;sklearn&#xff09;是一个用于机器学习的开源 Python 库。它建立在 NumPy、SciPy 和 matplotlib 等其他科学计算库之上&#xff0c;为机器学习的常见任务提供了简单…

sourceInsight常用设置和功能汇总(不断更新)(RGB、高亮、全路径、鼠标、宏、TODO高亮)

文章目录 必开配置设置背景颜色护眼的RGB值&#xff1f;sourceInsight4.0中如何设置选中某个单词以后自动高亮的功能&#xff1f;sourceinsight中输入设置显示全路径&#xff1f; 常用sourceInsight4.0中文乱码怎么解决&#xff0c;注意事项是什么&#xff1f;如何绑定鼠标中键…

oracle ogg学习和ogg常见变更操作

oracle ogg学习和ogg常见变更操作 OGG&#xff08;Oracle GoldenGate&#xff09; OGG 是一种基于日志的结构化数据复制软件&#xff0c;它通过解析源数据库在线日志或归档日志获得数据的增删改变化&#xff08;数据量只有日志的四分之一左右&#xff09; OGG 能够实现大量交…

游戏引擎学习第19天

介绍 这段内容描述了开发者在进行游戏开发时&#xff0c;对于音频同步和平台层的理解和调整的过程。以下是更详细的复述&#xff1a; 开发者表达了他希望今天继续进行的工作内容。他提到&#xff0c;昨天他讲解了一些关于音频的内容&#xff0c;今天他想稍微深入讲解一下他正…

Flink错误:一historyserver无法启动,二存在的文件会报错没有那个文件或目录

一.historyserver无法启动 historyserver执行了启动命令后却没有启动&#xff0c;而且也没有报错&#xff0c;如果日志无法启动的话网页8082是无法访问的 只能去查看日志 去flink的log文件查看日志&#xff1a; 发现应该是缺包了&#xff0c;导入jar包后可以解决 &#xff1a…

归一化/标准化对神经网络的训练是否有影响?

一、背景 归一化&#xff08;Normalization&#xff09;和标准化&#xff08;Standardization&#xff09;是数据预处理中的两种常见技术&#xff0c;旨在调整数据的范围和分布&#xff0c;以提高机器学习模型或者深度学习模型的性能和训练速度。虽然它们的目标相似&#xff0c…

Redis、TongRDS 可视化工具使用之 Redis Insight

题外话&#xff1a;除了可以连接 redis&#xff0c;也可以用来连接 TongRDS 1&#xff09;官网下载 Redis Insight 2&#xff09;安装 3&#xff09;连接 4&#xff09;使用 这里只是给一个使用例子

oracle查看锁阻塞-谁阻塞了谁

一 模拟锁阻塞 #阻塞1 一个会话正在往一个大表写入大量数据的时候&#xff0c;另一个会话加字段&#xff1a; #会话1 #会话2 会话2被阻塞了。 #阻塞2 模拟一个会话update一条记录&#xff0c;没提交。 另一个会话也update这一条记录&#xff1a; 会话2被阻塞了。 二 简单查…

django基于django的民族服饰数据分析系统的设计与实现

摘 要 随着网络科技的发展&#xff0c;利用大数据分析对民族服饰进行管理已势在必行&#xff1b;该平台将帮助企业更好地理解服饰市场的趋势&#xff0c;优化服装款式&#xff0c;提高服装的质量。 本文讲述了基于python语言开发&#xff0c;后台数据库选择MySQL进行数据的存储…

STM32单片机CAN总线汽车线路通断检测-分享

目录 目录 前言 一、本设计主要实现哪些很“开门”功能&#xff1f; 二、电路设计原理图 1.电路图采用Altium Designer进行设计&#xff1a; 2.实物展示图片 三、程序源代码设计 四、获取资料内容 前言 随着汽车电子技术的不断发展&#xff0c;车辆通信接口在汽车电子控…

golang对日期格式化

1.对日期格式化为 YYYY-mm-dd, 并且没有数据时&#xff0c;返回空 import ("encoding/json""time" )type DateTime time.Timetype SysRole struct {RoleId int64 gorm:"type:bigint(20);primary_key;auto_increment;角色ID;" json:&quo…

MySQL系列之数据授权(privilege)

导览 前言Q&#xff1a;如何对MySQL数据库进行授权管理一、MySQL的“特权”1. 权限级别2. 权限清单 二、授权操作1. 查看权限2. 分配权限3. 回收权限 结语精彩回放 前言 看过博主上一篇的盆友&#xff0c;可以Get到一个知识点&#xff1a;数据授权&#xff08;eg&#xff1a;g…

项目进度计划表:详细的甘特图的制作步骤

甘特图&#xff08;Gantt chart&#xff09;&#xff0c;又称为横道图、条状图&#xff08;Bar chart&#xff09;&#xff0c;是一种用于管理时间和任务活动的工具。 甘特图由亨利劳伦斯甘特&#xff08;Henry Laurence Gantt&#xff09;发明&#xff0c;是一种通过条状图来…

【Redis】Redis实现的消息队列

一、用list实现【这是数据类型所以支持持久化】 消息基于redis存储不会因为受jvm内存上限的限制&#xff0c;支持消息的有序性&#xff0c;基于redis的持久化机制&#xff0c;只支持单一消费者订阅&#xff0c;无法避免消息丢失。 二、用PubSub【这不是数据类型&#xff0c;是…