上傳背景介紹
在項目需求中,關于圖片、視頻、文件等上傳文件,一般不是直接放置在自己的后臺服務器上,一般都會購買云服務進行存儲。譬如阿里云的oss對象存儲。
那么,前端開發(fā)項目中,涉及到上傳的功能時,我們不是把文件上傳到自己的后臺服務器,而是阿里云上面去,然后拿到文件的訪問地址,例如圖片的地址,再傳遞給后臺保存下來,保存的是一個阿里云存儲地址。
那么,前端如何實現(xiàn)阿里云oss文件上傳功能呢?
官方文檔上說了:(阿里云oss文檔地址)
1、使用阿里云上傳的SDK來上傳到阿里云oss
2、不使用sdk的方式,直接使用post表單提交到阿里云oss去
3、小程序(忽略)
其實,也就兩種。要么使用阿里云給的sdk,他封裝的上傳方法等;要么使用表單提交,像提交到我們自己服務器一樣。
上傳方式1:表單直傳
Web端通過表單上傳方式直接上傳數(shù)據(jù)到OSS。
官方說有如下三種實現(xiàn)方式。
1、在客戶端通過JavaScript代碼完成簽名,然后通過表單直傳數(shù)據(jù)到OSS。詳情請參見JavaScript客戶端簽名直傳。
2、在服務端完成簽名,然后通過表單直傳數(shù)據(jù)到OSS。詳情請參見服務端簽名后直傳。
3、在服務端完成簽名,并且服務端設置了上傳后回調(diào),然后通過表單直傳數(shù)據(jù)到OSS。OSS回調(diào)完成后,再將應用服務器響應結果返回給客戶端。詳情請參見服務端簽名直傳并設置上傳回調(diào)。
我司采用的是第2中,服務端簽名后,前端在直傳oss。
可以理解為:上傳前,需要通過阿里云給的賬號生成鑰匙,然后前端拿到鑰匙后去開阿里云oss的門,不然,阿里云怎么你有沒有給他交錢呢?服務端簽名,就是把這個生成鑰匙的過程放到了自己后臺服務上,讓他們?nèi)グ谚€匙給我們。我們前端自己不保管這么敏感的資料。
具體可以看這里https://help.aliyun.com/document_detail/31926.htm?spm=a2c4g.11186623.0.0.3627344eM9Gwj8#concept-en4-sjy-5db
封裝的上傳示例代碼如下:
// fileUpload.js
import { v4 as uuidv4 } from 'uuid'
import axios from 'axios'
let basePath = 'www.yourapi.com' // 你的服務器接口域名
/**
* @description: 文件附件上傳
* file: 文件raw對象
* successCallback: 成功的回調(diào)函數(shù)
* errCallBack: 錯誤的回調(diào)函數(shù)
* progressCallback: 上傳進度的回調(diào)函數(shù)
* dir: 上傳阿里云目標文件夾 eg:圖片image,視頻video等
*/
const upload = function(file, successCallback = new Function(), errCallBack = new Function(), progressCallback = new Function(), dir = 'image') {
let fileName = file.name
axios({
method: 'get',
url: basePath + '/aliyun/get', // 請求簽名接口,找后臺要
params: {
dir: dir // 'image' // 這里的參數(shù),對應的就是上傳到那個文件夾下面,找后臺要
}
})
.then(res => {
// 拿到簽名信息后,組裝表單數(shù)據(jù),作參考,具體的字段找后臺要
let obj = res.data.data
let config = {}
config.host = obj['host']
config.policyBase64 = obj['policy']
config.accessid = obj['accessId']
config.signature = obj['signature']
config.expire = parseInt(obj['expire'])
config.callbackbody = obj['callback']
config.dir = obj['dir']
let fd = new FormData(),
uuid = uuidv4(),
key = config.dir + uuid
fd.append('key', key)
fd.append('success_action_status', '200')
fd.append('x-oss-object-acl', 'public-read')
fd.append('x-oss-meta-fullname', fileName)
fd.append('OSSAccessKeyId', config.accessid)
fd.append('policy', config.policyBase64)
fd.append('signature', config.signature)
fd.append('success_action_status', '200')
fd.append('file', file)
if (config.host.indexOf('http:') > -1) {
var protocol = window.location.protocol || 'http:'
var subUrl = config.host.substring(5, config.host.length)
config.host = protocol + subUrl
}
// 數(shù)據(jù)組裝完成后,發(fā)送上傳請求到阿里云oss
axios({
url: config.host,
method: 'POST',
data: fd,
processData: false,
cache: false,
contentType: false,
// 這里,我們可以做上傳經(jīng)度
onUploadProgress: function(progressEvent) {
if (progressEvent.lengthComputable) {
let percent = (progressEvent.loaded / progressEvent.total) * 100 || 0
progressCallback({
percent: percent
})
}
}
})
.then(() => {
// 拿到結果后,做其他操作
let size = file.size > 1000000 ? parseFloat(file.size / 1000000).toFixed(2) + 'M' : parseFloat(file.size / 1000).toFixed(2) + 'KB'
successCallback({
attachment: fileName,
aliyunAddress: key,
size: size,
host: config.host
})
})
.catch(err => {
errCallBack(err)
})
})
.catch(err => {
errCallBack(err)
})
}
export default upload
那么,在element-ui upload組件中使用自定義上傳功能:
<template>
<div class="text-msg-pic-upload">
<el-upload
:class="{ display: uploadDisabled }"
list-type="picture-card"
ref="upload"
action
multiple
:http-request="handleUpload"
:auto-upload="autoUpload"
:limit="limit"
:file-list="tempFileList"
:on-exceed="handleExceed"
:on-success="handleSuccess"
:on-remove="handleRemove"
:before-remove="beforeRemove"
:before-upload="beforeUpload"
:on-preview="handlePictureCardPreview"
accept="jpg,.jpeg,.png,.JPG,.JPEG"
>
<i class="el-icon-plus"></i>
<div slot="tip" class="el-upload__tip" v-if="tipsFlag">{{ tips }}</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible" append-to-body>
<img width="100%" :src="dialogImageUrl" alt="" />
</el-dialog>
<div class="Upload_pictures">
<ul class="el-upload__tip cBBBDBF" style="color: #BBBDBF;">
<li>支持PNG、JEPG格式 ,不超過2MB。</li>
</ul>
</div>
</div>
</template>
<script>
import upload from '@/utils/fileUpload.js'
export default {
name: 'UploadImageDemo',
props: {
width: {
type: String,
default: '240px'
},
autoUpload: {
type: Boolean,
default: true
},
limit: {
type: Number,
default: 3
},
limitType: {
type: Array,
default: () => ['image/jpeg', 'image/png', 'image/jpg']
},
disabled: {
type: Boolean,
default: false
},
imgList: {
type: Array,
default: () => []
},
tipsFlag: {
type: Boolean,
default: false
},
tips: {
type: String,
default: ''
}
},
data() {
return {
// 上傳文件列表,el-upload使用,臨時保存數(shù)據(jù)。
tempFileList: this.imgList,
host: '', // 阿里云上傳服務器地址根路徑
uploadDisabled: false,
dialogImageUrl: '',
dialogVisible: false
}
},
watch: {
// 解決第二渲染接口, 圖片還保留著原來的問題 JerryYi
imgList: {
immediate: true,
handler(val) {
this.tempFileList = val
}
}
},
computed: {
upText() {
return this.autoUpload ? '上傳文件' : '選擇文件'
}
},
created() {
},
mounted() {},
methods: {
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url
this.dialogVisible = true
},
beforeUpload(file) {
// console.log('beforeUpload', file)
let types = this.limitType
const isImage = types.includes(file.type)
const isLt20M = file.size / 1024 / 1024 < 10
if (!isImage) {
this.$message({
message: types.length == 0 ? '上傳圖片只能是 PNG 格式!' : '上傳圖片只能是 JPG、PNG 格式!',
type: 'warning'
})
return false
}
if (!isLt20M) {
this.$message.error('上傳圖片大小不能超過 1MB!')
return false
}
return isImage && isLt20M
},
// 自定義上傳操作
handleUpload(op) {
let dir = `images`
upload(
op.file,
res => {
let temp = {
name: res.attachment,
url: res.host + '/' + res.aliyunAddress
}
this.host = res.host
op.onSuccess(temp)
},
err => {
console.log(err)
},
res => {
op.onProgress(res)
},
dir
)
},
// 上傳成功后觸發(fā)
handleSuccess(response, file, fileList) {
this.filterFileFn(fileList)
},
// 返回給接口要用的格式
filterFileFn(fileList) {
let filterArr = fileList
.filter(item => !item.status || item.status !== 'ready') // 過濾未上傳的文件
.map(item => {
let url = item.response ? item.response.url : item.url
return {
url: url, // item.url || item.response.url
name: item.name
}
})
// console.log('fileList', fileList)
this.$emit('onSuccessFiles', filterArr)
},
// 監(jiān)聽移除文件列表
handleRemove(file, fileList) {
if (file.status === 'success') {
this.filterFileFn(fileList)
}
},
handleExceed(files, fileList) {
this.$message({ message: `當前限制選擇 ${this.limit} 個文件,本次選擇了 ${files.length} 個文件,共選擇了 ${files.length + fileList.length} 個文件`, type: 'warning' })
},
beforeRemove() {
// return this.$confirm(`確定移除 ${file.name}?`)
}
}
}
</script>
<style>
.text-msg-pic-upload .el-upload--picture-card,
.text-msg-pic-upload .el-upload-list--picture-card .el-upload-list__item {
width: 62px;
height: 62px;
line-height: 72px;
}
.display .el-upload--picture-card {
display: none;
}
</style>
使用:
<UploadImage :limit="9" :imgList="fileImgList" @onSuccessFiles="onSuccessImgFiles" />
// ...........其他略
data(){
return{
fileImgList: [{ name: 'test', url: 'http://testworldunion.oss-cn-shanghai.aliyuncs.com/scrm/1000/test/450821d6-13e1-464e-bc32-569fd277be2c.jpg' }] //圖片列表
}
},
methods: {
// 監(jiān)聽圖片上傳
onSuccessImgFiles(files) {
console.log('onSuccessImgFiles', files)
this.fileImgList = files
}
}
一句話: 使用el-upload的自定義上傳,結合我們封裝的函數(shù),實現(xiàn)上傳功能。
上傳方式二:阿里云oss SDK上傳
具體可以看這里:https://help.aliyun.com/document_detail/64047.htm?spm=a2c4g.11186623.0.0.119f3967Xq1Eb8#concept-64047-zh
這種方式,需要安裝依賴包或引入js文件。
npm install ali-oss
同樣的,我們封裝一下上傳:
import { v4 as uuidv4 } from 'uuid'
import axios from 'axios'
let basePath = 'www.yourapi.com' // 你的服務器接口域名
const OSS = require('ali-oss')
/**
* 阿里云oss sdk文件上傳
* @param {*} file 文件流
* @param {*} successCallback 成功回調(diào)
* @param {*} errCallBack 失敗回調(diào)
* @param {*} bucketName 阿里云桶名(可以指定多個桶名)
* @param {*} dir 上傳文件夾路徑 譬如images
*/
export function bucketUpload(file, successCallback = new Function(), errCallBack = new Function(), bucketName = '你的阿里云桶名', dir = 'image') {
let fileName = file.name
let pathName = window.location.host
let bucketNameTemp = bucketName
let requestData = {
bucket: bucketNameTemp,
dir: dir
}
// 先獲取上傳要的資料簽名
axios({
method: 'post',
url: basePath + '/aliyunsts', // 找后臺要接口,返回new OSS需要的參數(shù)
headers: {
'Content-Type': 'application/json'
},
data: requestData // 這里的參數(shù),看后臺要什么,溝通確定
})
.then(res => {
let obj = res.data || {}
let config = {}
// console.log(obj)
config.host = obj.OssUrl
// 實例化一個上傳客戶端
const client = new OSS({
// yourRegion填寫B(tài)ucket所在地域。Region填寫為oss-cn-hangzhou。
region: obj.OssRegion,
// 從STS服務獲取的臨時訪問密鑰(AccessKey ID和AccessKey Secret)。
accessKeyId: obj.AccessKeyId,
accessKeySecret: obj.AccessKeySecret,
// 從STS服務獲取的安全令牌(SecurityToken)。
stsToken: obj.SecurityToken,
// 填寫B(tài)ucket名稱。
bucket: obj.BucketName
})
try {
// 填寫Object完整路徑。Object完整路徑中不能包含Bucket名稱。
// 您可以通過自定義文件名(例如exampleobject.txt)或文件完整路徑(例如exampledir/exampleobject.txt)的形式實現(xiàn)將數(shù)據(jù)上傳到當前Bucket或Bucket中的指定目錄。
// data對象可以自定義為file對象、Blob數(shù)據(jù)或者OSS Buffer。
// 為保證唯一性,通過uuid將文件名替換
let uuid = uuidv4() + fileName.substring(fileName.lastIndexOf('.'))
if (dir.substring(dir.length - 1, 1) !== '/') {
dir += '/'
}
const result = client.put(dir + uuid, file)
result
.then(res => {
console.log(res)
let size = file.size > 1000000 ? parseFloat(file.size / 1000000).toFixed(2) + 'M' : parseFloat(file.size / 1000).toFixed(2) + 'KB'
successCallback({
attachment: fileName,
aliyunAddress: res.url,
size: size,
host: config.host
})
})
.catch(err => {
errCallBack(err)
})
} catch (e) {
console.log(e)
}
})
.catch(err => {
errCallBack(err)
})
}
對應的el-upload中的自定義上傳方法就改了:
handleUpload(op) {
let bucketName = 'myaliyunossbucketname' // 桶名
let dir = `images`
bucketUpload(
op.file,
res => {
let temp = {
name: res.attachment,
url: res.aliyunAddress
}
this.host = res.host
op.onSuccess(temp)
},
err => {
console.log(err)
},
bucketName,
dir
)
這種方式,有點缺陷,就是不能使用上傳進度。
如果要使用上傳進度,就需要使用分片上傳功能才行。
我們簡單上傳使用的這個put方法。不支持進度功能。
進度功能,需要使用另外一個方法:
所有的api方法可以參考:
https://www.npmjs.com/package/ali-oss文章來源:http://www.zghlxwxcb.cn/news/detail-455447.html
當然,也可以參考阿里云oss上傳的官方文檔:
https://help.aliyun.com/document_detail/383952.html文章來源地址http://www.zghlxwxcb.cn/news/detail-455447.html
到了這里,關于vue項目中上傳文件到阿里云oss方法的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關文章,希望大家以后多多支持TOY模板網(wǎng)!