上傳功能
在項目開發(fā)中,上傳文件的應(yīng)用場景非常的多,一般情況下,上傳的接口都是由公司內(nèi)部自己開發(fā)一套適用于任何場景都可以使用的接口,但是這意味著該接口所涉及到的工作量與技術(shù)點就非常的多,且不穩(wěn)定。于是大公司選擇了前者自己研發(fā),小公司則選擇了性價比更高的第三方云存儲服務(wù),國內(nèi)知名的云技術(shù)服務(wù)商基本上是 阿里云 和 騰訊云。以下是騰訊云存儲 COS 基本使用流程:
騰訊云存儲COS
? **對象存儲(Cloud Object Storage,COS)**是由騰訊云推出的無目錄層次結(jié)構(gòu)、無數(shù)據(jù)格式限制,可容納海量數(shù)據(jù)且支持 HTTP/HTTPS 協(xié)議訪問的分布式存儲服務(wù)。騰訊云 COS 的存儲桶空間無容量上限,無需分區(qū)管理,適用于 CDN 數(shù)據(jù)分發(fā)、數(shù)據(jù)萬象處理或大數(shù)據(jù)計算與分析的數(shù)據(jù)湖等多種場景。
首先進行登錄注冊 > 實名認證 > 公有讀私有寫
-
控制臺:https://console.cloud.tencent.com/cos/bucket
- 創(chuàng)建存儲桶對象,并完成相關(guān)配置項
- 存儲桶 CORS 跨域訪問控制:https://console.cloud.tencent.com/cos/bucket?bucket=hr-1305184603®ion=ap-guangzhou&type=securityconfig&anchorType=corsSetting
-
產(chǎn)品 API 文檔:https://cloud.tencent.com/document/product/436
-
SDK 下載列表:https://console.cloud.tencent.com/cos/sdk [也可 maven 配置 / npm 下載]
-
Secret 密鑰管理:https://console.cloud.tencent.com/cam/capi
使用示例
- 安裝 SDK :
npm install --save cos-js-sdk-v5
- 使用:
index.js
:
import COS from 'cos-js-sdk-v5'
// 實例化 COS 對象
const cos = new COS({
SecretID: 'AKIDWquiGXXGNIyOZU9YicGfohNnxsXw5zd1',
SecretKey: 'A4i6F8mHbfU3MPEn69wUnqdBUXLvFJ7f'
})
// 上傳配置
cos.putObject(
{
Bucket: 'hr-1305184603', // 存儲桶名稱
Region: 'ap-guangzhou', // 存儲桶所在地域
Key: file.uid + '', // 上傳的文件對象 id 唯一值,必須是字符串
StorageClass: 'STANDARD', // 固定值
Body: file, // 上傳的文件對象
onProgress: (progressData) => { // 上傳進度事件
/*
progressData: {
loaded: 1005180, // 已上傳文件大小
total: 1005180, // 總上傳文件大小
speed: 1100963.86, // 總用時
percent: 1 // 進度 1: 完成, 0: 未開始
}
*/
}
},
(err || data) => { // 上傳結(jié)果回調(diào)函數(shù)
// err 和 data :二者存其一,有 err 代表上傳失敗,有 data 代表上傳成功,接收一個返回值
/*
data: {
ETag: "", // 有效期標(biāo)記
Location: "hr-1305184603.cos.ap-guangzhou.myqcloud.com/1659251801441", // 上傳成功的圖片地址,可添加 http:// || https:// 前綴
headers: {content-length: '0'}, // header 頭部
statusCode: 200 // 狀態(tài)碼
}
*/
}
)
上傳組件封裝
uploadImg.vue
:
<template>
<div class="uploadImg">
<el-upload class="avatar-uploader" action="https://jsonplaceholder.typicode.com/posts/" :show-file-list="false" :before-upload="beforeAvatarUpload" :http-request="httpRequest" >
<img v-if="value" v-globalImgerror="defaultImg" :src="value" class="avatar" @click.stop.prevent="isShow = true" >
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
<!-- 刪除按鈕 -->
<div v-if="value" class="del" @click="del">X</div>
<!-- 預(yù)覽 -->
<el-dialog title="圖片預(yù)覽" width="500px" :visible.sync="isShow">
<img style="width: 100%" :src="value" alt="">
</el-dialog>
<transition name="fade">
<el-progress v-if="loading" :percentage="percentage" />
</transition>
</div>
</template>
<script>
import COS from 'cos-js-sdk-v5'
const cos = new COS({
SecretId: 'AKIDNXR8bpeSF1JL8EyZopxRfdHgqGUeuEn8',
SecretKey: 'HJ9mAbQEn7jgjY9swEI6yAamk7MN8hty'
})
export default {
name: 'UploadImg',
props: {
value: {
type: String,
default: ''
}
},
data() {
return {
defaultImg: require('@/assets/common/head.jpg'),
loading: false,
percentage: 0,
isShow: false
// imageUrl: ''
}
},
methods: {
beforeAvatarUpload(file) {
return true
},
httpRequest(res) {
this.loading = true
this.percentage = 0
cos.putObject(
{
Bucket:
'dhf481229-1305249343' /* 必須:存儲桶,使用自己注冊的存儲桶 */,
Region: 'ap-guangzhou' /* 存儲桶所在地域,必須字段 */,
Key: res.file.uid + '' /* 必須,要求是字符串 */,
StorageClass: 'STANDARD', // 固定,不用修改
Body: res.file, // 上傳文件對象
onProgress: (progressData) => {
// 上傳進度
this.percentage = progressData.percent * 100
if (progressData.percent === 1) {
// setTimeout(() => {
this.loading = false
// }, 1000)
}
console.log(JSON.stringify(progressData))
}
},
(err, data) => {
// 上傳成功的回調(diào) err:代表錯誤,沒有錯誤就代表成功 data:上傳成功的接口返回值
console.log(err || data)
this.$emit('input', 'http://' + data.Location)
// this.imageUrl = 'http://' + data.Location
}
)
},
// 刪除
del() {
this.$emit('input', '')
}
}
}
</script>
<style lang="scss" scoped>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
.uploadImg {
position: relative;
width: 178px;
&:hover {
.del {
display: block;
}
}
.del {
position: absolute;
top: 10px;
right: 10px;
cursor: pointer;
display: none;
}
.fade-enter-active,
.fade-leave-active {
transition: all 0.5s;
}
.fade-enter {
opacity: 0;
// transform: translateY(-100px);
}
.fade-leave-to {
opacity: 0;
// transform: translateY(100px);
}
}
</style>
index.vue
:
<template>
<div>
<h3>上傳v-model顯示上傳地址:{{ imgUrl }}</h3>
<UploadImg v-model="imgUrl" />
<el-upload class="avatar-uploader" action="#" :show-file-list="false" :on-success="handleAvatarSuccess" :before-upload="beforeAvatarUpload" :http-request="httpRequest">
<img v-if="imageUrl" :src="imageUrl" class="avatar" />
<i v-else class="el-icon-plus avatar-uploader-icon" />
</el-upload>
</div>
</template>
<script>
import COS from 'cos-js-sdk-v5'
const cos = new COS({
SecretId: 'AKIDWquiGXXGNIyOZU9YicGfohNnxsXw5zd1',
SecretKey: 'A4i6F8mHbfU3MPEn69wUnqdBUXLvFJ7f'
})
export default {
data() {
return {
imgUrl: '',
imageUrl: ''
}
},
methods: {
// 文件上傳成功回調(diào)
handleAvatarSuccess(res, file) {
console.log(res, file)
},
// 上傳之前回調(diào)
beforeAvatarUpload(file) {
const isPng = file.type === 'image/png' || file.type === 'image/jpeg'
const isLF2M = file.size <= 1024 * 1024 * 2
if (!isPng) {
this.$message.error('請上傳圖片文件')
return false
}
if (!isLF2M) {
this.$message.error('文件不得超過 2m')
return false
}
return true
},
//自定義上傳配置回調(diào)
httpRequest(res) {
// res: 封裝的文件對象
console.log(res)
cos.putObject(
{
Bucket: 'hr-1305184603' /* 必須:存儲桶 */,
Region: 'ap-guangzhou' /* 存儲桶所在地域,必須字段 */,
Key: res.file.uid + '' /* 必須,要求是字符串 */,
StorageClass: 'STANDARD', // 固定,不用修改
Body: res.file, // 上傳文件對象
onProgress: progressData => {
// 上傳進度
console.log(progressData)
}
},
(err, data) => {
// 上傳成功的回調(diào) err:代表錯誤,沒有錯誤就代表成功 data:上傳成功的接口返回值
console.log(err || data)
this.imageUrl = 'http://' + data.Location
}
)
}
}
}
</script>
<style>
.avatar-uploader .el-upload {
border: 1px dashed #d9d9d9;
border-radius: 6px;
cursor: pointer;
position: relative;
overflow: hidden;
}
.avatar-uploader .el-upload:hover {
border-color: #409eff;
}
.avatar-uploader-icon {
font-size: 28px;
color: #8c939d;
width: 178px;
height: 178px;
line-height: 178px;
text-align: center;
}
.avatar {
width: 178px;
height: 178px;
display: block;
}
</style>
下載文件相關(guān)場景
本地同源下載
鏈接下載
場景:同源下載文件。
解決:只需要一個
<a>
、<iframe>
標(biāo)簽即可。之所以要
<iframe>
標(biāo)簽是因為<a>
標(biāo)簽會進行跳轉(zhuǎn),所以需要在當(dāng)前頁面創(chuàng)建一個空的、隱藏的<iframe>
標(biāo)簽,讓 標(biāo)簽的 target 指向它,讓用戶無感的在內(nèi)部進行下載。
downloadFile(obj, name, suffix) {
const url = window.URL.createObjectURL(new Blob([obj]))
const iframe = document.createElement('iframe')
iframe.name = 'iframe'
iframe.style = 'display: none'
document.body.appendChild(iframe)
const link = document.createElement('a')
link.style.display = 'none'
link.href = url
link.target = 'iframe' // --不刷新、跳轉(zhuǎn)頁面
const fileName = parseTime(new Date()) + '-' + name + '.' + suffix
link.setAttribute('download', fileName)
document.body.appendChild(link)
link.click()
link.dispatchEvent(new MouseEvent("click"));
window.URL.revokeObjectURL(url)
document.body.removeChild(link)
},
文件流下載
場景:由后臺來返回文件流,需要前臺用 ajax
進行異步請求并處理
pdf:
const downloads = () => {
const url = pdf.url
const filename = pdf.title
const x = new XMLHttpRequest()
x.open('GET', url, true)
x.responseType = 'blob'
x.onload = () => {
// 會創(chuàng)建一個 DOMString,其中包含一個表示參數(shù)中給出的對象的URL。這個 URL 的生命周期和創(chuàng)建它的窗口中的 document 綁定。這個新的URL 對象表示指定的 File 對象或 Blob 對象。
const url = window.URL.createObjectURL(x.response)
const a = document.createElement('a')
a.href = url
a.download = filename
a.click()
a.dispatchEvent(new MouseEvent("click")); // 這里會觸發(fā)兩次 click,主要應(yīng)對 IE
window.URL.revokeObjectURL(url);
}
x.send()
}
圖片:
let url = "后端返回文件流的地址"
fetch(url,{method: 'get',headers:{'Content-Type': 'image/jpeg'}}).then(
res=>res.blob().then(blob=>{
var reader = new FileReader();
reader.readAsDataURL(blob); // 轉(zhuǎn)換為base64,可以直接放入a標(biāo)簽href
reader.onload = function (e) {
var down = document.createElement('a'); // 轉(zhuǎn)換完成,創(chuàng)建一個a標(biāo)簽用于下載
down.download = name;
down.href = String(e.target.result);
// document.appendChild(down)
down.click();
down.dispatchEvent(new MouseEvent("click"));
down.remove();
}
})
)
跨域下載文件
場景:后臺返回一個不同服務(wù)器的文件鏈接(不限于包括圖片、pdf文件),需要前端去下載到本地。這里引發(fā)了跨域問題。
解決方案:這個由前端去解決并不是很好,最好是可以讓后臺先下載好將 blob 數(shù)據(jù)流返回給前端去處理。文章來源:http://www.zghlxwxcb.cn/news/detail-732283.html
- 1、如果是固定的一個域名,建立一層
nginx
代理,前端訪問代理服務(wù)器來繞過跨域問題 - 2、最好的方案:由后臺去下載好文件,然后返回文件流給前端去處理(跨域 => 同源)。
處理代碼如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-732283.html
圖片文件處理
download(href, name) {
let eleLink = document.createElement('a')
eleLink.download = name
eleLink.href = href
eleLink.click()
eleLink.remove()
},
downloadByBlob(url, name) {
let image = new Image()
image.setAttribute('crossOrigin', 'anonymous')
image.src = url
image.onload = () => {
let canvas = document.createElement('canvas')
canvas.width = image.width
canvas.height = image.height
let ctx = canvas.getContext('2d')
ctx.drawImage(image, 0, 0, image.width, image.height)
canvas.toBlob(blob => {
let url = URL.createObjectURL(blob)
this.download(url, name)
// 用完釋放URL對象
URL.revokeObjectURL(url)
})
}
},
到了這里,關(guān)于Vue2-文件上傳、下載場景功能的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!