添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

当业务需求中需要上传大量的文件数据时,相比本地服务存储,第三方存储更能满足业务。比如 阿里云对象存储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: '' , // 上传的地址 paramsData: {}, // 上传的参数 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小时前
私信