今天背景說上傳檔案占帶寬,想辦法上傳弄前台吧,别走背景了,吃不住。是以有了如下探索。
步驟一:阿裡OSS配置
登入阿裡雲
homenew.console.aliyun.com/home/scene/…
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsISPrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdsATOfd3bkFGazxCMx8VesATMfhHLlN3XnxCMwEzX0xiRGZkRGZ0Xy9GbvNGLpZTY1EmMZVDUSFTU4VFRR9Fd4VGdsYTMfVmepNHLrJXYtJXZ0F2dvwVZnFWbp1zczV2YvJHctM3cv1Ce-cmbw5iYygzYwcjYxYjMhRmZkdjZiVzYwQmNjNGMidjYyQTOx8CX3AzLcFDMxIDMy8CXn9Gbi9CXzV2Zh1WavwVbvNmLvR3YxUjL4M3Lc9CX6MHc0RHaiojIsJye.png)
阿裡雲配置STS AssumeRole臨時授權通路OSS資源
- 使用者管理——>建立RAM子賬号,并自動生成AccessKeyId和AccessKeySecret;
- 政策管理——>建立自定義政策,授權通路OSS的bucket;
- 角色管理——>建立角色,将步驟2的政策授權給該角色;
- 政策管理——>建立自定義政策,授權通路STS的AssumeRole,該政策使用步驟3的角色;
- 使用者管理——>将步驟4的政策授權給步驟1 的子賬号。
- 使用子賬号的AccessKeyId、AccessKeySecret和角色的roleArn,roleSessionName可以自己随意起,申請STS臨時授權通路OSS。
步驟二:後端nest配置
ossUpload.service.ts
import { Injectable } from '@nestjs/common';import config from '../../../config';import * as STS from '@alicloud/sts-sdk';@Injectable()export class OssUploadService { /**
* 擷取 STS 認證
* @param username 登入使用者名
*/
async getIdentityFromSTS(username: string) {const sts = new STS({ endpoint: 'sts.aliyuncs.com',
...config,
}); const identity = await sts.getCallerIdentity(); // 打*号是因為涉及安全問題,具體角色需要詢問公司管理阿裡雲的同僚const stsToken = await sts.assumeRole(`acs:ram::${identity.AccountId}:role/ram***oss`, `${username}`);return { code: 200, data: {
stsToken,
}, msg: 'Success',
};
}
}複制代碼
ossUpload.module.ts
import { Module } from '@nestjs/common';import { OssUploadService } from './ossUpload.service';@Module({ providers: [OssUploadService], exports: [OssUploadService],
})export class OssUploadModule {}複制代碼
ossUpload.controller.ts
import { Controller, Body, Post, Request, UseGuards } from '@nestjs/common';import { OssUploadService } from './ossUpload.service';@Controller()export class OssUploadController { constructor(private readonly ossUploadService: OssUploadService) {} @Post('oss-upload') async getIdentityFromSTS(@Body() Body: any) {return await this.ossUploadService.getIdentityFromSTS(Body.username);
}
}複制代碼
報錯修複指南連結
help.aliyun.com/knowledge_d…help.aliyun.com/document_de…help.aliyun.com/knowledge_d…
步驟三:前端代碼
OSSFileUpload.vue
<script lang="ts">import { Component, Vue, Prop, Emit } from 'vue-property-decorator';import { getTokenFormLocal } from '@/utils/Storage';import { API_FILE_UPLOAD } from '@/api';import oss from '@/utils/ossForUpload.ts';/**
* 檔案上傳(aliOSS)
*
* @param {string} action - 上傳的位址
* @param {object} data - 上傳時附帶的額外參數
* @param {string} name - 上傳的檔案字段名
* @param {object} headers - 設定上傳的請求頭部
* @param {number} maxSize - 檔案上傳的大小上限 機關kb
* @param {number} limit - 最大允許上傳個數
* @param {string} fileTypes - 上傳檔案的類型
* @param {array} fileList - 上傳的檔案清單, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]
*
* @event on-success - 檔案上傳成功時的鈎子 function(response, file, fileList)
* @event on-remove - 檔案清單移除檔案時的鈎子 function(file, fileList)
* @event on-error - 檔案上傳錯誤 function(errorType, file) errorType: typeError 類型不比對;overSize: 超出大小上線; exceed: 超出最大個數
*/@Componentexport default class OSSFileUpload extends Vue { @Prop({default() { return []; }}) public fileList!: any[]; // 上傳的檔案清單, 例如: [{name: 'food.jpg', url: 'https://xxx.cdn.com/xxx.jpg'}]
@Prop({default() {return `${process.env.VUE_APP_BASE_API_URL}${API_FILE_UPLOAD}`; // 上傳接口
}}) public action!: string; // 必選參數,上傳的位址
@Prop({default() {return {};
}}) public data!: object; // 上傳時附帶的額外參數
@Prop() public name!: string; // 上傳的檔案字段名
@Prop({default() { return {identity: getTokenFormLocal() || '',
};
},
}) public headers!: object; // 設定上傳的請求頭部
@Prop(Number) public limit!: number; // 最大允許上傳個數
@Prop({default: 5 * 1024}) public maxSize!: number; // 檔案上傳的大小上線 機關kb
@Prop() public fileTypes!: string; public httpRequest = oss; public alertMessage = ''; // 錯誤資訊
@Emit() public onSuccess(response: any, file: any, fileList: any) {
} public beforeUpload(file: any) {
file.percentage = 20;const enabledSize = file.size / 1024;if (enabledSize > this.maxSize) { this.$nextTick(function() {this.alertMessage = `檔案大小不得超過${this.maxSize}kb`;
}); this.$emit('on-error-text', 'overSize', file); return false;
}this.alertMessage = '';
} /**
* 檔案超出個數限制時的鈎子
*/
public onExceed(files: any, fileList: any) {this.alertMessage = `超出最大上傳數量${this.limit}`;this.$emit('on-error', this.alertMessage, files, fileList);
} /**
* 檔案清單移除檔案時的鈎子
*/
@Emit() public onRemove(file: any, fileList: any) {this.alertMessage = '';
} public onError(err: any, file: any, fileList: any) {console.log('報錯了');this.alertMessage = process.env.NODE_ENV === 'production' ? '網絡異常,請稍後再試' : err;this.$emit('on-error', this.alertMessage, err, file, fileList);
} public uploadProcess(event: any, file: any, fileList: any) {// console.log('檔案上傳中');
}
}
</script><template>
<section><el-alert :style="{
marginBottom: '8px'
}" v-show="!!alertMessage" :title="alertMessage" type="error" show-icon></el-alert><el-upload :limit="limit" :action="action" :data="data" :name="name" :accept="fileTypes" drag multiple :file-list="fileList" :headers="headers" :on-success="onSuccess" :before-upload="beforeUpload" :on-progress="uploadProcess" :on-exceed="onExceed" :on-remove="onRemove" :http-request="httpRequest" :on-error="onError"> <i class="el-icon-upload"></i> <div class="el-upload__text">将檔案拖到此處,或<em>點選上傳</em></div> <div class="el-upload__tip" slot="tip"><slot name="tip"></slot></div></el-upload>
</section></template><style lang="less"></style>複制代碼
ossForUpload.ts
// 記得npm i ali-oss一下import * as OSS from 'ali-oss';import { getPcBasicOss } from '@/model';import ImgZipper from 'imgzipper';function getError(action: any, option: any, xhr: any) { let msg; if (xhr.response) {
msg = `${xhr.response.error || xhr.response}`;
} else if (xhr.responseText) {
msg = `${xhr.responseText}`;
} else {
msg = `fail to post ${action} ${xhr.status}`;
} const err: any = new Error(msg);
err.status = xhr.status;
err.method = 'post';
err.url = action; return err;
}function getBody(xhr: any) { const text = xhr.responseText || xhr.response; if (!text) {return text;
} try {return JSON.parse(text);
} catch (e) {return text;
}
}function zipImg(file: any) { try {const imageType = file.type.includes('image');if ( imageType ) { return new Promise((res, rej) => {
ImgZipper( file, (a: any, b: any) => { const zipfile = new File([a], file.name, {type: file.type, lastModified: Date.now()});
res(zipfile);
}, { scale: 0.78, // 生成圖像縮放大小 quality: 0.62, // 生成圖像的品質 disableBlob: null, // canvas.toBlob方法失敗後調用函數 type: 'image/jpeg', // 生成圖像格式 exif: true, // 是否使用調整相機旋轉});
});
}
} catch (err) {throw err;
}
}export default async function upload(option: any) { const {file = {}} = option; const newFile: any = await zipImg(file); try {// 這裡改成我們node的請求const { credentials = {} } = await getPcBasicOss({ bucket: 'asal' });const client = new OSS({ region: credentials.region, accessKeyId: credentials.AccessKeyId, accessKeySecret: credentials.AccessKeySecret , stsToken: credentials.SecurityToken, bucket: 你的bucket,
});const result = await client.put(`/l-web-pc/${newFile.name}`, newFile);return result;
} catch (err) {
option.onError(err);throw err;
}
}複制代碼