当业务需求中需要上传大量的文件数据时,相比本地服务存储,第三方存储更能满足业务。比如
阿里云对象存储OSS
(Object Storage Service)。它是一款分布式云存储服务。那上传数据到OSS有什么好处呢?稳定、可靠、安全、低成本。非常适合用来存储各种非结构化数据,比如视频、图像、日志、文本文件。
OSS使用:
第三方存储服务基本上都是收费的,所以先让领导或Devops人员去申请开通账号。完事登录阿里云控制台,进行服务基础配置。附:
开发指南
为了实现跨域访问,保证跨域数据传输的安全进行,需要在OSS控制台正确设置跨域CORS规则:
技术方案:
目前通过Web端将文件上传到OSS,有以下三种方案:
利用OSS Browser.js SDK将文件上传到OSS
该方案通过OSS Browser.js SDK直传数据到OSS,详细的SDK Demo请参见
上传文件
。在网络条件不好的状况下可以通过断点续传的方式上传大文件。该方案在个别浏览器上有兼容性问题,目前兼容IE10及以上版本浏览器,主流版本的Edge、Chrome、Firefox、Safari浏览器,以及大部分的Android、iOS、WindowsPhone手机上的浏览器。
使用表单上传方式将文件上传到OSS
利用OSS提供的PostObject接口,通过表单上传的方式将文件上传到OSS。该方案兼容大部分浏览器,但在网络状况不好的时候,如果单个文件上传失败,只能重试上传。操作方法请参见
PostObject上传方案
。
通过小程序上传文件到OSS
通过小程序,如微信小程序和支付宝小程序,利用OSS提供的PostObject接口来实现表单上传。操作方式请参见
微信小程序直传实践
和
支付宝小程序直传实践
。
最佳实践:
Web端上传数据至OSS,我个人采用的是 “
服务端签名后直传
”(在服务端完成签名,然后通过表单直传数据到OSS。),优势在于无需将Accesskey暴露在前端页面,具有更高的安全性。
PC端上传文件到阿里云OSS(Vue+Ant Design UI)
关键代码示范:
<a-upload v-decorator=
"['mediaItems', { rules: [{ required: true, message: '媒体资源不能为空' }] }]"
accept=
".jpg, .jpeg, .png, .gif, .mp4, .mov"
:
data
=
"paramsData"
list-type=
"picture-card"
multiple :file-list=
"fileList"
:showUploadList=
"false"
:before-upload=
"beforeUpload"
:custom-request=
"customRequest"
> <div :
class
=
"
$style
.antUploadLayout"
> <a-icon type=
"plus"
/> <span :
class
=
"
$style
.antUploadText"
>上传资源</span> </div></a-upload>
data
() {
return
{
action:
''
,
fileList: [], }
methods: {
beforeUpload(file) {
const
fileType = [
'image/jpg'
,
'image/jpeg'
,
'image/png'
,
'image/gif'
,
'video/mp4'
,
'video/quicktime'
]
const
isImageOrVideo = fileType.includes(file.type)
if
(!isImageOrVideo) {
this
.$message.error(
'资源格式不正确!'
)
return
false
}
const
isLt100M = file.size /
1024
/
1024
<
50
if
(!isLt100M) {
this
.$message.error(
'资源大小不允许超过50MB!'
)
return
false
}
return
this
.getToken(file, file.name) }, getToken(file, fileName) {
return
new Promise((resolve, reject) => {
this
.getStsToken({ fileName }) .then((res) => {
const
{ key, policy, ossRamAccessKeyId, signature, stsToken, ossHost, expire, publicUrl } = res
this
.action = ossHost
this
.paramsData = { key, success_action_status:
'200'
, OSSAccessKeyId: ossRamAccessKeyId, policy, Signature: signature,
'x-oss-security-token'
: stsToken,
'x-oss-content-type'
:
'multipart/form-data'
, Expires: expire, publicUrl, } resolve(file) }) .
catch
(() => {
this
.$message.error(
'上传失败,请稍后再试'
) reject() }) }) }, customRequest(fileInfo) {
const
{ file } = fileInfo
this
.uploadResource({ url:
this
.action.replace(
'http://'
,
'https://'
),
data
: { ...
this
.paramsData, file }, }) .then(async () => {
const
{
data
: { key, publicUrl }, file: { uid, name, type }, } = fileInfo
const
uploadFile = { uid: `-${uid}`, name, status:
'done'
, url: publicUrl, thumbUrl: publicUrl, type, filePath: key, fileType: type.indexOf(
'video/'
) !== -
1
? MediaType.VIDEO : MediaType.IMAGE, }
this
.fileList = await [...
this
.fileList, uploadFile].filter((x) => x.status ===
'done'
) await
this
.form.setFieldsValue({ mediaItems:
this
.fileList.map((item) => ({ fileType: item.type.indexOf(
'video/'
) !== -
1
? MediaType.VIDEO : MediaType.IMAGE, fileName: item.name, filePath: item.filePath, })), }) }) .
catch
(async () => {
this
.$message.error(
'上传失败,请稍后再试'
)
this
.fileList = await
this
.fileList.filter((x) => x.status ===
'done'
) }) },},
微信小程序上传文件到阿里云OSS(Taro+React+TypeScript)
关键代码示范:
<
Button
className={styles.
uploadButton
} onClick={
this
.
handleOpenUpload
}>
<
RIcon
type
=
"add"
className
=
{styles.icon}
/>
<
Text
>
{tips || '点击上传'}
</
Text
>
</
Button
>
enum
FILE_TYPE
{
IMAGE
=
'IMAGE'
,
VIDEO
=
'VIDEO'
,}
interface
Media
{
fileType
:
FILE_TYPE
fileName
:
string
filePath
:
string
url
:
string
}
type
State
= {
mediaList
:
Media
[]}
state
:
State
= {
mediaList
: [],}
handleUpload =
(
type
) =>
{
if
(
type
===
FILE_TYPE
.
IMAGE
) {
chooseImage
({
sizeType
: [
'original'
],
success
:
(
res
) =>
{ res.
tempFiles
.
forEach
(
async
({ path, size }) => {
const
index = path.
lastIndexOf
(
'.'
)
const
isFileType = [
'jpg'
,
'jpeg'
,
'png'
,
'gif'
].
includes
(path.
substring
(index +
1
))
if
(!isFileType) { messager.
error
(
'资源格式不正确!'
)
return
false
}
const
isLt100M = size /
1024
/
1024
<
50
if
(!isLt100M) { messager.
error
(
'资源大小不允许超过50MB!'
)
return
false
}
await
this
.
uploadMedia
(path,
type
) }) }, }) }
else
{
chooseVideo
({
compressed
:
false
,
success
:
(
res
) =>
{
const
{ tempFilePath, size } = res
const
index = res.
tempFilePath
.
lastIndexOf
(
'.'
)
const
isFileType = [
'mp4'
,
'mov'
,
'MOV'
].
includes
(res.
tempFilePath
.
substring
(index +
1
))
if
(!isFileType) { messager.
error
(
'资源格式不正确!'
)
return
false
}
const
isLt100M = size /
1024
/
1024
<
50
if
(!isLt100M) { messager.
error
(
'资源大小不允许超过50MB!'
)
return
false
}
this
.
uploadMedia
(tempFilePath,
type
) }, }) }},
uploadMedia =
async
(path,
type
) => {
const
header = {
Authorization
:
getStorageSync
(
ACCESS_TOKEN
), } messager.
showLoading
(
'上传中'
,
true
)
const
fileName = path.
replace
(
'http://tmp/'
,
''
).
replace
(
'wxfile://'
,
''
).
replace
(
'://'
,
''
)
const
{ key, policy, ossRamAccessKeyId, signature, stsToken, ossHost, expire, publicUrl, } =
await
dispatch.
user
.
getStsToken
({ fileName })
const
formData = { key,
success_action_status
:
'200'
,
OSSAccessKeyId
: ossRamAccessKeyId, policy,
Signature
: signature,
'x-oss-security-token'
: stsToken,
'x-oss-content-type'
:
'multipart/form-data'
,
Expires
: expire, publicUrl, }
uploadFile
({
url
: ossHost,
filePath
: path, header,
name
:
'file'
, formData,
success
:
() =>
{
const
media = {
fileType
:
type
, fileName,
filePath
: key,
url
: publicUrl, }
this
.
setState
( {
mediaList
: [...
this
.
state
.
mediaList
, media], } ) },
fail
:
() =>
{ messager.
error
(
'上传失败,请重试'
) },
complete
:
() =>
{ messager.
hideLoading
() }, }) }
记录于 2022-03-01 周二 上海
14小时前
-
2.8w
-
winter
JavaScript
函数式编程
-
730
-
一介4188
TypeScript
JavaScript