1、文件上傳核心流程
- 選擇文件??:用戶通過拖拽或點擊選擇文件
- 手動觸發上傳??:點擊"確定"按鈕后開始上傳(阻止自動上傳)
- ??獲取上傳憑證??:從后端獲取華為云OBS的上傳配置
- 構建表單數據??:按照華為云要求組織表單數據
- 執行上傳??:發送POST請求到華為云OBS
- 返回結果??:處理上傳結果并返回文件信息
2、關鍵參數說明
參數 | 說明 | 來源 |
---|---|---|
file | 要上傳的文件對象 | 用戶選擇 |
businessName | 業務分類名稱 | 組件props傳入 |
privateMode | 是否私有模式 | 默認為true |
sourceType | 數據源類型('ka'/'oin') | 組件props傳入 |
bucket | OBS桶名稱 | 后端接口返回 |
endpoint | OBS服務端點 | 后端接口返回 |
policy | 上傳策略 | 后端接口返回 |
signature | 簽名 | 后端接口返回 |
accessId | 訪問密鑰ID | 后端接口返回 |
3、部分字段解釋
1、業務名稱 (businessName)??
??作用??:
- ??文件分類存儲??:用于在華為云OBS中創建業務專屬目錄,實現文件按業務線分類存儲
- ??權限隔離??:不同業務文件可以設置不同的訪問權限
- 檢索過濾??:便于后續按業務維度查詢和管理文件
// 上傳到路徑:kafile/訂單業務/2023/08/30/時間戳.jpg
pathParts = ['kafile', '訂單業務', '2023/08/30', '1693388800000.jpg']// 如果沒有業務名稱則上傳到:kafile/2023/08/30/時間戳.jpg
pathParts = ['kafile', '2023/08/30', '1693388800000.jpg']
??2、數據源類型 (sourceType)??
??作用??:
- 多賬戶切換??:對接不同華為云賬號或服務端點
- 差異化處理??:不同來源的文件可能需要不同的上傳策略
- 擴展性??:預留的枚舉字段方便后續接入新存儲系統
3、為什么需要這些參數???
1.主要用來區分調不同服務的接口來獲取華為云OBS的上傳憑證???
- 同一系統可能服務多個業務線(如大客戶、oin等)
- 不同業務對文件存儲的要求不同(如訂單需要長期保存,臨時文件只需保留7天)
2.為什么需要調用后端接口獲取華為云OBS的上傳憑證?
華為云OBS的直傳需要以下安全憑證:(接口返回)
{bucket: "your-bucket-name", // 存儲桶名稱endpoint: "obs.cn-south-1.myhuaweicloud.com", // 區域端點policy: "eyJleHBpcmF0aW9uIjo...", // 經過Base64編碼的上傳策略signature: "Dq1YpZxlQODhQwM...", // 對policy的簽名accessId: "AKIDEXAMPLE" // 臨時訪問密鑰ID
}
安全原因??:直接在前端存儲華為云AK/SK是極度危險的,必須通過后端中轉。
3.為什么還需要拼接上傳url?
在華為云OBS(對象存儲服務)中,拼接上傳URL (https://${bucket}.${endpoint}
) 是??華為云OBS API的強制要求??,這種設計涉及到底層的訪問機制和安全策略。
華為云OBS的訪問URL遵循特定格式:
https://[bucket名稱].[區域端點]
4.為什么需要獲取當前日期格式的目錄?
getCurrentDateDir()
?是一個用于 ??按日期自動生成文件存儲目錄?? 的工具函數。
自動組織文件存儲結構??
將文件按日期分目錄存儲,最終路徑如:
kafile/業務名/2023/8/30/文件名.ext
4、代碼實現
<template><a-upload-dragger:max-count="maxCount"//最大上傳文件數:before-upload="beforeUpload"//上傳前的處理函數v-model:fileList="fileList"//雙向綁定文件列表數據 ><p>點擊或拖拽文件上傳</p></a-upload-dragger><a-button type="link" @click="handleOk">確定</a-button>
</template><script setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue';
import dayjs from 'dayjs';const props = defineProps({maxCount: Number,// 最大上傳文件數量businessName: String,// 業務名稱sourceType: { type: String, default: 'ka' }// 數據源類型,默認'ka'
});const emits = defineEmits(['handleUpload']);const fileList = ref([]);// 阻止自動上傳的函數,返回false表示阻止默認上傳行為
const beforeUpload = () => false;// 處理確定按鈕點擊
const handleOk = async () => {if (!fileList.value.length) {// 檢查是否有文件被選中message.warning('請先選擇文件');return;}try {// 獲取第一個文件的原始文件對象const file = fileList.value[0].originFileObj;// 調用上傳函數并等待結果const result = await uploadToHuaweiOBS({file,// 文件對象businessName: props.businessName,// 業務名稱sourceType: props.sourceType// 數據源類型});emits('handleUpload', {bucketName: result.bucketName,// 存儲桶名稱oriFileName: result.originalName,// 原始文件名fileName: result.fileNameWithoutDir,// 不含目錄的文件名file,// 文件對象dir: 'kafile'// 存儲目錄});message.success('文件上傳成功');} catch (error) {message.error(`上傳失敗: ${error.message}`);}
};// 華為云OBS上傳函數
const uploadToHuaweiOBS = async ({ file, businessName = '', sourceType = 'ka' }) => {// 1. 調用后端接口獲取華為云OBS的上傳憑證const authData = await getUploadAuth(sourceType);// 2. 準備上傳參數const { bucket, endpoint, policy, signature, accessId } = authData;// 構建上傳URLconst url = `https://${bucket}.${endpoint}`;// 3. 構建文件路徑const fileDir = getCurrentDateDir(); // 獲取當前日期格式的目錄const fileName = generateFileName(file.name); // 生成新文件名const folder = 'kafile'; // 設置存儲目錄// 構建完整路徑數組const pathParts = [folder, fileDir, fileName];// 如果有業務名稱,插入到路徑中if (businessName) pathParts.splice(1, 0, businessName);// 拼接完整文件路徑const fileKey = pathParts.join('/');// 4. 構建表單數據用來做文件上傳const formData = new FormData();// 按照華為云要求的順序添加字段formData.append('key', fileKey);// 文件路徑formData.append('policy', policy);// 上傳策略formData.append('AccessKeyId', accessId);// 訪問密鑰IDformData.append('signature', signature);// 簽名formData.append('file', file);// 文件本身// 5. 執行上傳(這里是直傳華為云OBS,注意不是后端接口)await axios.post(url, formData);// 6. 返回結果(這里是華為云OBS返回的參數)return {fileUrl: `${url}/${fileKey}`,// 完整文件URLfileName,// 生成的文件名bucketName: bucket,// 存儲桶名稱originalName: file.name,// 原始文件名fileNameWithoutDir: pathParts.slice(1).join('/'),// 不含根目錄的路徑relativePath: fileKey,// 相對路徑businessName// 業務名稱};
};// 輔助函數
// 如需補零格式(推薦):使用 'YYYY/MM/DD'
const getCurrentDateDir= () => {return dayjs().format('YYYY/MM/DD'); // 輸出示例: "2023/08/30"
};// 生成帶時間戳的新文件名
const generateFileName = (originalName) => {const ext = originalName.split('.').pop();// 獲取文件擴展名return `${Date.now()}.${ext}`;// 時間戳+擴展名
};
</script>
5、組件使用
<template><HuaweiUpload:max-count="3"business-name="customer-service"source-type="oin"@handle-upload="handleUpload"/>
</template><script setup>
import { ref } from 'vue';
import { message } from 'ant-design-vue';const uploading = ref(false);const handleUpload = async (fileInfo) => {uploading.value = true;const formData = new FormData()//創建表單數據對象for (const key in data) {//遍歷數據對象并添加到表單formData.append(key, data[key])}try {//調用自己的業務邏輯const { data } = await weeklyImport(formData)if (data.importStatus === '1') {useMessage().success('導入成功')} else if (data.importStatus === '2') {useMessage().error('導入失敗,請查看歷史記錄')}} catch (error) {useMessage().warning('導入失敗,請查看歷史記錄')} finally {//隱藏加載狀態uploading.value = false}
};
</script>