草庐IT

web直传cos(腾讯云)图片&视频

qq_38969618 2023-07-01 原文

1. 腾讯云控制台:登录 - 腾讯云

2. 腾讯云对象存储jssdk文档(含cos-js-sdk-v5.min.js文件 + 跨域设置访问配置):对象存储 快速入门-SDK 文档-文档中心-腾讯云33下载与安装相关资源对象存储COS的XMLJSSDK源码下载地址:XMLJavaScriptSDK。SDK快速下载地址:XMLJavaScriptSDK。演示示例Demo下载地址:XMLJavaScrihttps://cloud.tencent.com/document/product/436/11459

1. JavaScript SDK 常见问题: 对象存储 JavaScript SDK 常见问题-SDK 文档-文档中心-腾讯云

一、封装cos工具函数(类)

class CosUtil {
    uploadConfigInfo = {} // cos相关临时密钥等配置信息{ bucket, host, allow_prefix, xxx }
    cosInstance = null // cos实例
    constructor() {
        this.init()
    }
    init() {
        this.getTempCosUploadConfig()
    }
    /**
     * @method 获取COS的临时密钥配置信息
     * @return { { allow_prefix, host, bucket, credentials, xxx } } 注意:allow_prefix,存放的目录前缀名,最后是带有斜杠的,如:"web_xx/xx/", host: "https://xxx.xx.com"
    */
    async getTempCosUploadConfig() {
        const currentAccessHost = window.location.host
        const uploadAapiHostMap = { // 不同域名对于后台接口URL的映射
            'test.com': 'https://xxx.com',
            'pre.com': 'https://xxx.com',
            'onl.com': 'https://xxx.com',
        }
        const currentApiHost = uploadAapiHostMap[currentAccessHost]
        const applicationHeaders = { appId: xxx, appKey: 'xxxx' } // 本公司后台接口需要,用于区分不同上传方式。如:web还是app等
        const params = { client_type: 'xx', app_name: 'xxx'}
        const res = await service({ // service函数是公司内部封装的axios请求方法
            headers: applicationHeaders,
            url: currentApiHost + '/xx/xxx/getTempKeys',
            method: 'post',
            data: params
        })
        this.uploadConfigInfo = res.data
        this.getCosInstance()
    }
    // 获取COS实例
    getCosInstance() {
        const that = this
        // 初始化COS实例
        this.cosInstance = new COS({
            // getAuthorization 必选参数
            getAuthorization: function (options, callback) {
                const { start_time, expired_time, credentials } = that.uploadConfigInfo
                callback({
                    TmpSecretId: credentials.tmp_secret_id,
                    TmpSecretKey: credentials.tmp_secret_key,
                    SecurityToken: credentials.session_token,
                    // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
                    StartTime: start_time, // 时间戳,单位秒,如:1580000000
                    ExpiredTime: expired_time, // 时间戳,单位秒,如:1580000000
                });
            }
        });
    }
    /**
     * 上传图片或视频
     * @param { String } storePath 文件存放路径 - 注意:开头不要带斜杠/, 直接是文件名,或目录名/xx/文件名
     * @param { File } file 文件对象
     * @param { Element } progressDom dom对象 - 页面上需要实时显示上传进度的dom元素,选填
    */
    handleUploadFile(storePath, file, progressDom) {
        const that = this
        return new Promise((resolve, reject) => {
            const { bucket, region, allow_prefix } = that.uploadConfigInfo
            const fullStorePath = allow_prefix + storePath
            that.cosInstance.uploadFile({
                Bucket: bucket, /* 填写自己的bucket,必须字段 */
                Region: region,     /* 存储桶所在地域,必须字段 */
                Key: fullStorePath,           /* 存储在桶里的对象键(例如:1.jpg,a/b/test.txt,图片.jpg)支持中文,必须字段 */
                Body: file, // 上传文件对象
                SliceSize: 1024 * 1024 * 5,     /* 触发分块上传的阈值,超过5MB使用分块上传,小于5MB使用简单上传。可自行设置,非必须 */
                onProgress: function(progressData) {
                    const { percent } = progressData // percent取值:是0-1之间的含小数,如0.55。
                    progressDom && (progressDom.innerHTML = percent * 100)
                },
                onFileFinish: function (err, data, options) {   /* 非必须 */
                    console.log(options.Key + '上传' + (err ? '失败' : '完成')); // 如:web_xx/xx/xx/test.1675235009017.png上传失败
                },
            }, function(err, data = {}) {
                if (err) {
                    console.log('上传失败', Object.prototype.toString.call(err), err.message, data)
                    that.handleCOSCallFail(err.message)
                    reject(err)
                } else {
                    console.log('上传成功', data, fullStorePath)
                    resolve({ ...data, filePath: fullStorePath })  // 如: { statusCode: 200, Location: "xxx-test-1314993711.cos.ap-shanghai.myqcloud.com/web_xx/xpx/xxx/test.1675245328509.png", filePath: "web_xx/xxx/xxx/test.1675245328509.png" }
                }
            });
        })
    }
    // 处理页面停留过长再操作时,配置会失效的问题
    handleCOSCallFail(errMessage = '') {
        const configExpiredText = 'Request has expired';
        console.log('校验报错信息中是否含配置过期标志', errMessage.indexOf(configExpiredText))
        if (errMessage.indexOf(configExpiredText) > -1) {
            const confirmResult = window.confirm('COS配置信息已失效, 需要刷新页面!您确定要刷新该页面吗?')
            if (confirmResult == true) {
                window.location.reload() // 手动刷新页面,重新获取密钥
            }
        } else {
            alert(errMessage)
        }
    }
    /**
     * @method 删除文件
     * @param { String } storePath 文件存放路径 - 注意:开头不要带斜杠/, 直接是文件名,或目录名/xx/文件名
    */
    handleDeleteFile(storePath) {
        const that = this
        return new Promise((resolve, reject) => {
            const { bucket, region, allow_prefix } = that.uploadConfigInfo
            const fullStorePath = allow_prefix + storePath
            that.cosInstance.deleteObject({
                Bucket: bucket, /* 填写自己的bucket,必须字段 */
                Region: region,     /* 存储桶所在地域,必须字段 */
                Key: fullStorePath,              /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
            }, function(err, data) {
                if(err) {
                    console.log('删除文件失败', Object.prototype.toString.call(err), err.message, data)
                    that.handleCOSCallFail(err.message)
                    reject(err)
                } else {
                    resolve(data)
                }
            });
        })
        
    }
}

 二、在html中使用

小提示:第一次上传到cos某个目录(存储桶bucket下的某个自建目录)时,如果没有手动提前在cos后台创建该目录时,第一次代码上传时,cos后台会自动创建对应目录。

<!-- 引入腾讯云cos的sdk(官网文件,下载到本地引入) -->
<script src="js/cos-js-sdk-v5.min.js"></script>

<script type="text/javascript" src="js/cos-utils.js"></script>
<script language="JavaScript">
    var cosClientObj = new CosUtil()
    console.log('实例化的cos工具对象', cosClientObj, cosClientObj.handleUploadFile)
</script>

old实现方式如下(含:cookie缓存临时密钥,过期后自动重新获取逻辑):未封装成类,复用性差!

一、在.html中使用cos

<!-- 引入腾讯云cos的sdk(官网下载到本地) -->
<script src="js/cos-js-sdk-v5.min.js"></script>
... // 省略code
<script type="module" language="JavaScript">
    import { Cookie } from '/xxx/js/utils.js'
    const webUploadCookieKey = 'xpx_web_upload_config'
    const applicationHeaders = { appId: xxx, appKey: 'xxx' } // 后台需要的传参
    let uploadConfigInfo = {}
    $(document).ready(function() {
       const uploadConfig = Cookie.get(webUploadCookieKey)
       if (uploadConfig) {
        uploadConfigInfo = JSON.parse(uploadConfig) || {}
        const isExpired = getCurrentSecondUnitOfTimeStamp() >= uploadConfigInfo.expired_time
        console.log('腾讯云cos临时密钥是否过期', isExpired, uploadConfigInfo)
        if (isExpired) {
            getTempCosUploadConfig()
        }
       } else {
            getTempCosUploadConfig()
       }
    });
     // 获取当前时间戳(秒级)
     function getCurrentSecondUnitOfTimeStamp () {
        return Date.parse(new Date() + '') / 1000
    }
    // 初始化COS实例
    var cos = new COS({
        // getAuthorization 必选参数
        getAuthorization: function (options, callback) {
            const { start_time, expired_time, credentials } = uploadConfigInfo
            callback({
                TmpSecretId: credentials.tmp_secret_id,
                TmpSecretKey: credentials.tmp_secret_key,
                SecurityToken: credentials.session_token,
                // 建议返回服务器时间作为签名的开始时间,避免用户浏览器本地时间偏差过大导致签名错误
                StartTime: start_time, // 时间戳,单位秒,如:1580000000
                ExpiredTime: expired_time, // 时间戳,单位秒,如:1580000000
            });
        }
    });
    // 调后台接口:获取COS上传临时密钥的配置信息
    async function getTempCosUploadConfig() {
        const params = { client_type: 'web', app_name: 'xxx'}
        const res = await service({ // 封装的axios请求
            headers: applicationHeaders,
            url: currentApiHost + '/xxx/upload/getTempKeys',
            method: 'post',
            data: params
        })
        const { expired_time } = res.data
        const validSecond = expired_time - getCurrentSecondUnitOfTimeStamp()
        Cookie.set(webUploadCookieKey, JSON.stringify(res.data), { maxAge: validSecond })
        uploadConfigInfo = res.data
    }
    // 上传图片&视频
    function handleFileInUploading(fileName, file, progressDom, goodsId) {
        return new Promise((resolve, reject) => {
            const { bucket, region, allow_prefix } = uploadConfigInfo
            const uploadKey = goodsId ? `${allow_prefix}goodsVideos/${goodsId}-${fileName}` : (`${allow_prefix}goodsImages/${fileName}`)
            cos.uploadFile({
                Bucket: bucket, /* 填写自己的bucket,必须字段 */
                Region: region,     /* 存储桶所在地域,必须字段 */
                Key: uploadKey,           /* 存储在桶里的对象键(例如:1.jpg,a/b/test.txt,图片.jpg)支持中文,必须字段 */
                Body: file, // 上传文件对象
                SliceSize: 1024 * 1024 * 5,     /* 触发分块上传的阈值,超过5MB使用分块上传,小于5MB使用简单上传。可自行设置,非必须 */
                onProgress: function(progressData) {
                    console.log('上传文件进度为:', JSON.stringify(progressData));
                    const { percent } = progressData // percent取值:是0-1之间的含小数,如0.55。
                    progressDom && (progressDom.innerHTML = percent * 100)
                },
                onFileFinish: function (err, data, options) {   /* 非必须 */
                    console.log(options.Key + '上传' + (err ? '失败' : '完成'));
                },
            }, function(err, data) {
                console.log('上传的结果是:', err, data)
                if (err) {
                    console.log('上传失败', err);
                    handleCOSFail(err.message)
                    reject(err)
                } else {
                    // 注意:返回的Location字段就是文件的ulr,但是没有'http(s)://'头,需自己手动加。
                    console.log('上传成功', err, data); // null, { Location:'test-xx/xx/.png', ... }
                    resolve(data) 
                }
            });
        })
    }
    
    // 处理页面停留过长再操作时,配置会失效的问题 & 提示其他报错信息
    function handleCOSFail(errMessage) {
        const configExpiredText = 'Request has expired';
        if (errMessage.indexOf(configExpiredText) > -1) {
            const confirmResult = window.confirm('COS配置信息已失效, 需要刷新页面!您确定要刷新该页面吗?')
            if (confirmResult == true) {
                Cookie.remove(webUploadCookieKey)
                window.location.reload() // 手动刷新页面,重新获取密钥
            }
        } else {
            alert(errMessage)
        }
    }

    // 删除已上传的文件
    function handleDeleteUploadedFile(goodsId, fileName) {
        return new Promise((resolve, reject) => {
            const { bucket, region, allow_prefix } = uploadConfigInfo
            const uploadKey = goodsId ? `${allow_prefix}goodsVideos/${goodsId}-${fileName}` : (`${allow_prefix}goodsImages/${fileName}`)
            cos.deleteObject({
                Bucket: bucket, /* 填写自己的bucket,必须字段 */
                Region: region,     /* 存储桶所在地域,必须字段 */
                Key: uploadKey,              /* 存储在桶里的对象键(例如1.jpg,a/b/test.txt),必须字段 */
            }, function(err, data) {
                console.log(err || data);
                if(err) {
                    console.log('删除文件失败', err)
                    handleCOSFail(err.message)
                    reject(err)
                } else {
                    console.log('删除文件成功', data) // {statusCode: 204, ... }
                    resolve(data)
                }
            });
        })
        
    }

二、工具函数cookie封装(设置、获取、删除) 

// utils.js文件

export const Cookie = {
    /** 设置cookie
     * @Params { key, value, options } // 如:setCookie('token', 'xxxx', { maxAge: 24 * 60 * 60 })
     * @Note 1. options参数:maxAge(单位是秒,即多少秒后cookie过期)、path、secret、expires
     * @Note 2. 客户端设置domain是无效的,因为只能是当前域
     * @Note 3. 设置cookie的存储值为对象时,需先JSON.stringify(obj)。
     * @Docuement https://www.cnblogs.com/yesyes/p/15352703.html
    */
    set: function (key, value, options) {
        // 注意:encodeURIComponent(str) 是用来编码, 获取cookie时,需使用decodeURIComponent(str)解码
        let cookie = encodeURIComponent(key) + '=' + encodeURIComponent(value) + '; ';
        for(let k in options) {
            // 首字母大写,驼峰转下换线
            const newkey = k.replace(/^\S/, function($1) {
                return $1.toUpperCase();
            }).replace(/\B[A-Z]/g,function($1) {
                return '-' + $1
            })
            if(newkey === 'Secure') {
                cookie += 'Secure; '
            }else{
                cookie += newkey +'=' + options[k] + '; '
            }
        }
        document.cookie = cookie
    },
    
    get: function (cookieKey) {
        if (document.cookie.length > 0) {
            var arr = document.cookie.split('; ') // 这里显示的格式需要切割一下自己可输出看下
            for (let i = 0; i < arr.length; i++) {
                let arr2 = arr[i].split('=') // 再次切割
                // 判断查找相对应的值
                if (arr2[0] === cookieKey) {
                    return decodeURIComponent(arr2[1]) // 需要先解码,再返回。
                }
            }
        }
    },
 
    remove: function(cookieKey) {
        Cookie.set(cookieKey, '', { maxAge: 0 }); // 删除指定的cookie
    }
};

 三、项目中遇到的问题&解决

 1、调用cos的deleteObject()方法时,报错提示:Access Denied

答:因公司后台接口返回的临时密钥(使用子账号生成的),没有配置删除的权限( "name/cos:DeleteObject")导致。具体参考:腾讯COS – 使用临时密钥下载私有桶对象失败Cos Error Code: AccessDenied【踩坑】 – foam

 四、参考链接

有关web直传cos(腾讯云)图片&视频的更多相关文章

  1. ruby-on-rails - rails : "missing partial" when calling 'render' in RSpec test - 2

    我正在尝试测试是否存在表单。我是Rails新手。我的new.html.erb_spec.rb文件的内容是:require'spec_helper'describe"messages/new.html.erb"doit"shouldrendertheform"dorender'/messages/new.html.erb'reponse.shouldhave_form_putting_to(@message)with_submit_buttonendendView本身,new.html.erb,有代码:当我运行rspec时,它失败了:1)messages/new.html.erbshou

  2. ruby-on-rails - 由于 "wkhtmltopdf",PDFKIT 显然无法正常工作 - 2

    我在从html页面生成PDF时遇到问题。我正在使用PDFkit。在安装它的过程中,我注意到我需要wkhtmltopdf。所以我也安装了它。我做了PDFkit的文档所说的一切......现在我在尝试加载PDF时遇到了这个错误。这里是错误:commandfailed:"/usr/local/bin/wkhtmltopdf""--margin-right""0.75in""--page-size""Letter""--margin-top""0.75in""--margin-bottom""0.75in""--encoding""UTF-8""--margin-left""0.75in""-

  3. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  4. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  5. ruby - 检查 "command"的输出应该包含 NilClass 的意外崩溃 - 2

    为了将Cucumber用于命令行脚本,我按照提供的说明安装了arubagem。它在我的Gemfile中,我可以验证是否安装了正确的版本并且我已经包含了require'aruba/cucumber'在'features/env.rb'中为了确保它能正常工作,我写了以下场景:@announceScenario:Testingcucumber/arubaGivenablankslateThentheoutputfrom"ls-la"shouldcontain"drw"假设事情应该失败。它确实失败了,但失败的原因是错误的:@announceScenario:Testingcucumber/ar

  6. ruby-on-rails - Rails 3.2.1 中 ActionMailer 中的未定义方法 'default_content_type=' - 2

    我在我的项目中添加了一个系统来重置用户密码并通过电子邮件将密码发送给他,以防他忘记密码。昨天它运行良好(当我实现它时)。当我今天尝试启动服务器时,出现以下错误。=>BootingWEBrick=>Rails3.2.1applicationstartingindevelopmentonhttp://0.0.0.0:3000=>Callwith-dtodetach=>Ctrl-CtoshutdownserverExiting/Users/vinayshenoy/.rvm/gems/ruby-1.9.3-p0/gems/actionmailer-3.2.1/lib/action_mailer

  7. ruby-on-rails - 如何优雅地重启 thin + nginx? - 2

    我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server

  8. ruby - 在 jRuby 中使用 'fork' 生成进程的替代方案? - 2

    在MRIRuby中我可以这样做:deftransferinternal_server=self.init_serverpid=forkdointernal_server.runend#Maketheserverprocessrunindependently.Process.detach(pid)internal_client=self.init_client#Dootherstuffwithconnectingtointernal_server...internal_client.post('somedata')ensure#KillserverProcess.kill('KILL',

  9. ruby - 主要 :Object when running build from sublime 的未定义方法 `require_relative' - 2

    我已经从我的命令行中获得了一切,所以我可以运行rubymyfile并且它可以正常工作。但是当我尝试从sublime中运行它时,我得到了undefinedmethod`require_relative'formain:Object有人知道我的sublime设置中缺少什么吗?我正在使用OSX并安装了rvm。 最佳答案 或者,您可以只使用“require”,它应该可以正常工作。我认为“require_relative”仅适用于ruby​​1.9+ 关于ruby-主要:Objectwhenrun

  10. ruby - 无法让 RSpec 工作—— 'require' : cannot load such file - 2

    我花了三天的时间用头撞墙,试图弄清楚为什么简单的“rake”不能通过我的规范文件。如果您遇到这种情况:任何文件夹路径中都不要有空格!。严重地。事实上,从现在开始,您命名的任何内容都没有空格。这是我的控制台输出:(在/Users/*****/Desktop/LearningRuby/learn_ruby)$rake/Users/*******/Desktop/LearningRuby/learn_ruby/00_hello/hello_spec.rb:116:in`require':cannotloadsuchfile--hello(LoadError) 最佳

随机推荐