赞
踩
<dependency>
<groupId>com.huaweicloud</groupId>
<artifactId>esdk-obs-java</artifactId>
<version>3.23.5</version>
</dependency>
package com.pig4cloud.pig.admin.controller; import com.obs.services.ObsClient; import com.obs.services.model.*; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.*; import org.springframework.web.multipart.MultipartFile; import java.io.*; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; /** * 切片上传 */ @RestController @AllArgsConstructor @RequestMapping("/upload") public class SysUploadController { private final ObsClient obsClient; /** * 启动上传任务 */ @GetMapping("/getUploadId/{objectKey}") public String getUploadId(@PathVariable("objectKey")String objectKey) { InitiateMultipartUploadRequest request = new InitiateMultipartUploadRequest("<桶名称>", objectKey); ObjectMetadata metadata = new ObjectMetadata(); metadata.addUserMetadata("property", "property-value"); metadata.setContentType("text/plain"); request.setMetadata(metadata); InitiateMultipartUploadResult result = obsClient.initiateMultipartUpload(request); String uploadId = result.getUploadId(); return uploadId; } /** * 分片上传 */ @PostMapping("/chunk") public Map<String,String> splitFileUpload( @RequestParam("objectKey")String objectKey, @RequestParam("file") MultipartFile file, @RequestParam("chunk") int chunk, @RequestParam("uploadId") String uploadId) throws Exception { File file1 = multipartFileToFile(file); Map<String,String> map = uploadChunk(uploadId, file1, chunk, objectKey); return map; } /** * 合并上传 */ @PostMapping("/completeUpload") public CompleteMultipartUploadResult completeUpload( @RequestParam("objectKey")String objectKey, @RequestParam("uploadId") String uploadId, @RequestBody List<Map<String,String>> mapList ) { List<PartEtag> partEtags = new ArrayList<>(); for(Map<String,String> map: mapList ){ PartEtag part1 = new PartEtag(); part1.setPartNumber(Integer.valueOf(map.get("partNumber"))); part1.seteTag(map.get("etag")); partEtags.add(part1); } CompleteMultipartUploadRequest request = new CompleteMultipartUploadRequest( "<桶名称>", objectKey, uploadId, partEtags); CompleteMultipartUploadResult result = obsClient.completeMultipartUpload(request); return result; } /** * 取消任务上传 */ @GetMapping("/cancelUpload") public void cancelUpload( @RequestParam("objectKey")String objectKey, @RequestParam("uploadId") String uploadId ){ AbortMultipartUploadRequest request = new AbortMultipartUploadRequest("<桶名称>", objectKey, uploadId); obsClient.abortMultipartUpload(request); } public Map<String,String> uploadChunk(String uploadId, File file,int chunk, String objectKey){ // Endpoint以北京四为例,其他地区请按实际情况填写。 Map<String,String> map = new HashMap<>(); UploadPartRequest request = new UploadPartRequest("<桶名称>", objectKey); request.setUploadId(uploadId); request.setPartNumber(chunk); request.setFile(file); request.setPartSize(5 * 1024 * 1024L); UploadPartResult result = obsClient.uploadPart(request); map.put("etag",result.getEtag()); map.put("partNumber",String.valueOf(result.getPartNumber())); return map; } /** * MultipartFile 转 File * * @param file * @throws Exception */ public static File multipartFileToFile(MultipartFile file) throws Exception { File toFile = null; if (file.equals("") || file.getSize() <= 0) { file = null; } else { InputStream ins = null; ins = file.getInputStream(); toFile = new File(file.getOriginalFilename()); inputStreamToFile(ins, toFile); ins.close(); } return toFile; } //获取流文件 private static void inputStreamToFile(InputStream ins, File file) { try { OutputStream os = new FileOutputStream(file); int bytesRead = 0; byte[] buffer = new byte[8192]; while ((bytesRead = ins.read(buffer, 0, 8192)) != -1) { os.write(buffer, 0, bytesRead); } os.close(); ins.close(); } catch (Exception e) { e.printStackTrace(); } } }
package com.pig4cloud.pig.admin.config; import com.obs.services.ObsClient; import com.pig4cloud.plugin.oss.OssProperties; import lombok.AllArgsConstructor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration @AllArgsConstructor public class ObsConfiguration { // 请在yml里配置或使用 @Value 单独获取 private final OssProperties ossProperties; @Bean public ObsClient obsClient() { return new ObsClient(ossProperties.getAccessKey(), ossProperties.getSecretKey(), ossProperties.getEndpoint()); } }
<template> <div class="upload-container"> <div class="fileUpload"> <div class="item-upload"> <span v-if="required" class="required">*</span> <el-button type="primary" οnclick="document.getElementById('fpfileName').click()">文件选择</el-button> <input type="file" id="fpfileName" class="file2" style="display: none" @change="selectFile" /> </div> <div v-if="showFileName" class="item-info"> {{fileName}} </div> <div v-else class="item-info"> 支持{{getAcceptType()}}文件格式 </div> <div v-if="progress>0" class="item-cancel"> <el-button style="margin-top: 5px" size="small" type="danger" @click="cancelUploadInfo">X</el-button> </div> </div> <div class="item-process" v-if="progress>0"> <el-progress :text-inside="true" :stroke-width="20" :percentage="progress"/> </div> <div v-if="requiredValid" class="item-valid"> 请上传文件 </div> </div> </template> <script> import { getUploadId , uploadFileInfo, completeUpload, cancelUpload, getFileUrl} from "@/api/admin/client.js" import { defineComponent, reactive, toRefs, onMounted } from "vue"; import { ElMessage } from 'element-plus' export default defineComponent({ name: "file-upload", props:{ //文件接受类型 accept:{ type: Array, default: ['doc','docx','pdf'] }, //是否必填 required:{ type: Boolean, default: false }, //文件限制大小 fileSize:{ type: Number, default: 500 } }, setup(props) { const data =reactive({ file: null, fileName: "", fileSize: 0, progress: 0, objectKey: "", chunkSize: 5*1024 * 1024, totalChunks: 0, currentChunk: 1, uploadId: "", partArr: [], showFileName: false, bucketName: "", objectKeyName: "", required: props.required, requiredValid: false }) // 选择文件 const selectFile = (event)=>{ data.file = event.target.files[0]; event.target.value = '' if(data?.file?.size){ const fileSize = Math.round(data?.file?.size/1024/1024*100)/100 if(fileSize > props.fileSize){ ElMessage.warning("文件大小超过上限"+props.fileSize+"MB") return } data.fileSize = fileSize console.log(data.fileSize) const fileName = data.file.name const index = fileName.lastIndexOf("."); const fileType = fileName.substring(index+1, fileName.length) if(!props.accept.includes(fileType)){ ElMessage.warning("文件类型不支持") return } data.progress = 0; data.fileName = data.file.name data.showFileName = true data.totalChunks = Math.ceil(data.file.size / data.chunkSize) data.objectKey = guid()+"."+fileType getUploadId(data.objectKey).then((res)=>{ data.uploadId = res.data data.progress = 1; uploadFile(); }) } } // 文件上传 const uploadFile = ()=>{ const index = data.currentChunk -1 const start = index * data.chunkSize; const end = Math.min((index + 1) * data.chunkSize, data.file.size); const formData = new FormData() formData.append('file', data.file.slice(start, end)) formData.append('chunk', data.currentChunk) formData.append('objectKey', data.objectKey) formData.append('uploadId', data.uploadId) // 调用后端接口上传切片数据 uploadFileInfo(formData).then((res) => { data.partArr.push(res.data) console.log(data.progress) data.currentChunk++; data.progress = Math.floor((data.currentChunk / data.totalChunks) * 100); if (data.currentChunk <= data.totalChunks) { uploadFile(); } else { // 所有切片上传完成 data.progress = 99; console.log(data.progress) complete(); } }).catch((error) => { initParam() console.error('切片上传失败:', error); ElMessage.warning("文件上传异常或上传已取消") }); } // 上传完成并合并分段上传 const complete = () =>{ completeUpload(data.partArr,data.objectKey,data.uploadId).then((res) => { console.log('文件上传完成'); console.log(res); data.bucketName = res.data.bucketName data.objectKeyName = res.data.objectKey data.progress = 100; initParam() }).catch((error) => { console.error('文件上传失败:', error); ElMessage.error("文件上传失败") }); } // 取消上传 const cancelUploadInfo = () =>{ if(data.progress !== 100){ cancelUpload(data.objectKey,data.uploadId).then(()=>{ data.progress = 0; data.showFileName = false initParam() }) }else{ data.progress = 0; data.showFileName = false initParam() } } const getAcceptType = () =>{ let str = ""; if(props.accept){ for(let item of props.accept){ str += item +"," } } return str.substr(0,str.length-1) } // 构建uuid const guid =() => { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) { var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8); return v.toString(16); }); } // 初始化参数 const initParam = ()=>{ data.file = null; data.objectKey = "" data.totalChunks = 0 data.currentChunk = 1 data.uploadId = "" data.partArr = [] data.requiredValid = false } // 必传校验 const validRequired = ()=>{ if(data.bucketName && data.objectKeyName){ data.requiredValid = false return true }else{ data.requiredValid = true return false } } // 返回上传结果 桶名称、文件名、文件大小 const getUploadInfo = () =>{ return { bucketName: data.bucketName, fileName: data.objectKeyName, fileSize: data.fileSize } } // 初始化 onMounted(()=>{ data.progress = 0; }) return{ ...toRefs(data), selectFile, uploadFile, getAcceptType, validRequired, getUploadInfo, cancelUploadInfo } } }) </script> <style scoped> .upload-container{ .fileUpload { display: flex; flex-direction: row; padding: 0px 8px; .item-upload { width: 100px; display: flex; flex-direction: row; align-items: center; justify-content: center; .required{ color: red; padding: 0px 5px; } } .item-info { flex: 1; padding-top: 12px; font-size: 13px; } .item-cancel{ width: 30px; padding-right: 20px; } } .item-process{ margin-top: 5px; padding: 0px 23px; box-sizing: border-box; } .item-valid{ color: red; padding: 0px 10px; font-size: 13px; } } </style>
// 获取uploadId export function getUploadId(objectKey) { return request({ url: '/admin/upload/getUploadId/' + objectKey, method: 'get' }) } // 分段上传 export function uploadFileInfo( data) { return request({ url: '/admin/upload/chunk', method: 'post', headers:{'Content-Type': 'multipart/form-data'}, data: data }) } // 分段合并 export function completeUpload( data,objectKey,uploadId) { return request({ url: '/admin/upload/completeUpload?objectKey='+objectKey+'&uploadId='+uploadId, method: 'post', data: data }) } // 取消上传 export function cancelUpload(objectKey,uploadId) { return request({ url: '/admin/upload/cancelUpload?objectKey='+objectKey+'&uploadId='+uploadId, method: 'get' }) }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。