深入掌握 OSS,最完美的 OSS 上传方案!

文件上传是常见需求,一般我们不会把文件直接上传到应用服务器,因为单台服务器存储空间是有限的,不好扩展。

我们会用单独的 OSS (Object Storage Service)对象存储服务来上传下载文件。

比如一般会买阿里云的 OSS 服务。

我们本地文件存储是目录-文件的组织方式:

深入掌握 OSS,最完美的 OSS 上传方案!_第1张图片

而 OSS 服务的存储结构是这样的:

深入掌握 OSS,最完美的 OSS 上传方案!_第2张图片

一个桶里放一些文件。

阿里云 OSS 的控制台也提到了对象存储没有目录层级结构:

深入掌握 OSS,最完美的 OSS 上传方案!_第3张图片

但下面明明是支持目录的呀:

深入掌握 OSS,最完美的 OSS 上传方案!_第4张图片

这其实只是模拟实现的。

Object 会存储 id、文件内容、元数据三部分信息:

深入掌握 OSS,最完美的 OSS 上传方案!_第5张图片

阿里云 OSS 只是用元信息部分模拟实现了目录。

就像打了个 tag 一样,并不是说文件存储在这个 tag 下,只是你可以用这个 tag 来检索文件。

深入掌握 OSS,最完美的 OSS 上传方案!_第6张图片

除了对象存储 OSS,阿里云也提供了文件存储和块存储的方式:

深入掌握 OSS,最完美的 OSS 上传方案!_第7张图片

块存储就是把整块磁盘给你用,你需要自己格式化,存储容量有限。

文件存储就是有目录层次结构,你可以上传下载文件,存储容量有限。

对象存储就是 key-value 存储,分布式的方式实现的,存储容量无限。

这些简单了解就行,绝大多数情况下,我们都是用 OSS 对象存储。

我们买个阿里云的 OSS 服务来试试看:

深入掌握 OSS,最完美的 OSS 上传方案!_第8张图片

我买了 40G 的 OSS 国内通用资源包,花了 5 块钱。

然后我们创建个 Bucket(桶):

深入掌握 OSS,最完美的 OSS 上传方案!_第9张图片

在北京创建了一个 Bucket,文件就会存储在那里的服务器上。

设置公共读,也就是这些文件大家都可以直接访问。

不然私有的方式,你访问每个文件都要带上一些身份信息:

深入掌握 OSS,最完美的 OSS 上传方案!_第10张图片

有的同学说,不是静态文件要在全国各地都能访问到么?存在北京的服务器会不会访问速度慢?

这是 CDN 的活:

深入掌握 OSS,最完美的 OSS 上传方案!_第11张图片

接入 CDN 后,访问该域名会走到云服务的 DNS,然后返回一台最近的缓存服务器的地址,这台服务器会从源站拿文件来缓存,之后就不再访问源站。

这里的源站就可以是 OSS 服务。

深入掌握 OSS,最完美的 OSS 上传方案!_第12张图片

创建 Bucket 之后,我们上传个文件试试:

深入掌握 OSS,最完美的 OSS 上传方案!_第13张图片

上传完之后在文件列表就可以看到这个文件了:

深入掌握 OSS,最完美的 OSS 上传方案!_第14张图片

点开可以看到文件详情,用这个 URL 就可以访问:

深入掌握 OSS,最完美的 OSS 上传方案!_第15张图片

当然,生产环境下我们不会直接用 OSS 的 URL 访问,而是会开启 CDN,用网站域名访问,最终回源到 OSS 服务:

深入掌握 OSS,最完美的 OSS 上传方案!_第16张图片

在控制台里上传很简单,那如果想在代码里上传呢?

官方文档里有示例代码,我们试试看:

深入掌握 OSS,最完美的 OSS 上传方案!_第17张图片

mkdir oss-test
cd oss-test
npm init -y

深入掌握 OSS,最完美的 OSS 上传方案!_第18张图片

安装用到的包:

npm install ali-oss

写下代码:

const OSS = require('ali-oss')

const client = new OSS({
    region: 'oss-cn-beijing',
    bucket: 'guang-333',
    accessKeyId: '',
    accessKeySecret: '',
});

async function put () {
  try {
    const result = await client.put('cat.png', './mao.png');
    console.log(result);
  } catch (e) {
    console.log(e);
  }
}

put();

region 在概览里可以看到:

深入掌握 OSS,最完美的 OSS 上传方案!_第19张图片

这里的 accessKeyId 和 acessKeySecret 是什么呢?

本来我们身份认证都是通过用户名密码:

深入掌握 OSS,最完美的 OSS 上传方案!_第20张图片

但这样不够安全,所以我们创建了 accessKey 用来代表身份,用它来做身份认证,就算泄漏了,也不影响别的:

深入掌握 OSS,最完美的 OSS 上传方案!_第21张图片

我们创建个 accesKey:

深入掌握 OSS,最完美的 OSS 上传方案!_第22张图片

深入掌握 OSS,最完美的 OSS 上传方案!_第23张图片

创建完成后,拿到 accesKeyId 和 accessKeySecret 后运行代码:

深入掌握 OSS,最完美的 OSS 上传方案!_第24张图片

这里的 mao.png 是这样的:

深入掌握 OSS,最完美的 OSS 上传方案!_第25张图片

在控制台可以看到上传成功了:

深入掌握 OSS,最完美的 OSS 上传方案!_第26张图片

这就是 OSS 用 api 上传文件的用法。

只是我们刚刚用的 accessKey 不够安全。

打开 accessKey 管理页面的时候就提示了:

深入掌握 OSS,最完美的 OSS 上传方案!_第27张图片

让我们不要直接用 accessKey,而是创建一个子用户再创建 accessKey。

那我们就创建个子用户:

深入掌握 OSS,最完美的 OSS 上传方案!_第28张图片

深入掌握 OSS,最完美的 OSS 上传方案!_第29张图片

然后用这个 accessKey 的 id 和 secret 就好了:

深入掌握 OSS,最完美的 OSS 上传方案!_第30张图片

但你直接换上它还不行:

深入掌握 OSS,最完美的 OSS 上传方案!_第31张图片

会提示你 403,没有权限。

需要你授权下:

深入掌握 OSS,最完美的 OSS 上传方案!_第32张图片

深入掌握 OSS,最完美的 OSS 上传方案!_第33张图片

把 OSS 的管理和读取权限给这个子用户:

深入掌握 OSS,最完美的 OSS 上传方案!_第34张图片

然后再试下:

深入掌握 OSS,最完美的 OSS 上传方案!_第35张图片

这时候就上传成功了。

回过头来看下,不得不说阿里云在安全这一块设计的就很巧妙。

如果我们直接用用户名密码验证呢:

深入掌握 OSS,最完美的 OSS 上传方案!_第36张图片

那万一泄漏了不就完蛋了么?

但是如果创建个 accessKey 用它来做身份认证:

深入掌握 OSS,最完美的 OSS 上传方案!_第37张图片

就算泄漏了我也可以禁用啊:

深入掌握 OSS,最完美的 OSS 上传方案!_第38张图片

再进一步,直接用这个 accessKey 它是有所有权限的。

我们先创建个 RAM 子用户,再分配给他某些权限,这样就算泄漏了,是不是能做的事情就更少了?

当然就更安全。

深入掌握 OSS,最完美的 OSS 上传方案!_第39张图片

所以说,阿里云这套 accessKey 和 RAM 子用户的身份认证方式,还是很不错的。

再说回 OSS,一般的文件直接上传就行,涉及到大文件就要分片上传了。

分片上传实现原理是前端常见面试题了,大家都能答上来。

就是把文件用 slice 方法分成一个个小的片,然后全部上传完之后请求一个接口合并分片。

阿里云的大文件分片上传也是这样实现的:

深入掌握 OSS,最完美的 OSS 上传方案!_第40张图片

具体怎么用直接看文档就好了,这里我们就不试了:

深入掌握 OSS,最完美的 OSS 上传方案!_第41张图片

有了 OSS 服务之后,我们上传文件还需要经过应用服务器么?

可以经过也可以不经过。

如果经过应用服务器,那就要客户端上传文件之后,我们在服务里接受文件,上传 OSS:

这样当然是可以的,还能保护 accessKey 不被人窃取。

只是会浪费应用服务器的流量。

那如果不经过呢?深入掌握 OSS,最完美的 OSS 上传方案!_第42张图片

在客户端用 accessKey 把文件传到 OSS,之后把 URL 传给应用服务器就好了。

这样减少了应用服务器的流量消耗,但是增加了 accessKey 暴露的风险。

各有各的坏处。

那有没有啥两全其美的办法呢?

有。

深入掌握 OSS,最完美的 OSS 上传方案!_第43张图片

阿里云的文档里也提到了这个问题。

它给出的解决方案就是生成一个临时的 accessKey 来用。

代码是这样的:

const OSS = require('ali-oss')

async function main() {

    const config = {
        region: 'oss-cn-beijing',
        bucket: 'guang-333',
        accessKeyId: 'LTAI5tDemEBPwQkTx65jZCdy',
        accessKeySecret: 'I0vHYOoqIC78lH7A5c5XB1H7Pev7bp',
    }

    const client = new OSS(config);
    
    const date = new Date();
    
    date.setDate(date.getDate() + 1);
    
    const res = client.calculatePostSignature({
        expiration: date.toISOString(),
        conditions: [
            ["content-length-range", 0, 1048576000], //设置上传文件的大小限制。      
        ]
    });
    
    console.log(res);
    
    const location = await client.getBucketLocation();
    
    const host = `http://${config.bucket}.${location.location}.aliyuncs.com`;

    console.log(host);
}

main();

上传 OSS 的地址,用的临时 accessKey 和 signature 和 policy 都有了:

深入掌握 OSS,最完美的 OSS 上传方案!_第44张图片

这些代码不用记,文档里都有:

深入掌握 OSS,最完美的 OSS 上传方案!_第45张图片

这样就能在网页里用这些来上传文件到 OSS 了:

创建个 index.html




    
    
    Document
    


    
    
    

这里 getOSSInfo 应该是请求服务端的接口,拿到刚才我们控制台输出的那些东西。

这里就简化下,直接写死在代码里了。

引入 axios,用这些信息来上传文件。

跑个静态服务器:

npx http-server .

深入掌握 OSS,最完美的 OSS 上传方案!_第46张图片

这时候你上传文件的时候会提示跨域错误:

深入掌握 OSS,最完美的 OSS 上传方案!_第47张图片

我们在控制台开启下跨域:

深入掌握 OSS,最完美的 OSS 上传方案!_第48张图片

然后再试下:

深入掌握 OSS,最完美的 OSS 上传方案!_第49张图片

上传成功了!

控制台文件列表也可以看到这个文件:

深入掌握 OSS,最完美的 OSS 上传方案!_第50张图片

因为临时的 accessKey 过期时间很短,我们设置的是一天,所以暴露的风险也不大。

这样服务端就根本没有接受文件的压力,只要等客户端上传完之后,带上 URL 就好了。

案例代码上传了 github:https://github.com/QuarkGluonPlasma/nestjs-course-code/tree/main/oss-test

总结

上传文件一般不会直接存在服务器目录下,这样不好扩展,一般我们会用阿里云的 OSS,它会自己做弹性扩展,所以存储空间是无限的。

OSS 对象存储是在一个 bucket 桶下,存放多个文件。

它是用 key-value 存储的,没有目录的概念,阿里云 OSS 的目录只是用元信息来模拟实现的。

我们在测试了在控制台的文件上传,也测试过了 node 里用 ali-oss 包来上传、在网页里直传 OSS 这三种上传方式。

不管在哪里上传,都需要 acessKeyId 和 acessKeySecret。

这个是阿里云的安全策略,因为直接用用户名密码,一旦泄漏就很麻烦,而 acessKey 泄漏了也可以禁用。而且建议用 RAM 子用户的方式生成 accessKey,这样可以最小化权限,进一步减少泄漏的风险。

客户端直传 OSS 的方式不需要消耗服务器的资源,但是会有泄漏 acessKey 的风险,所以一般都是用服务端生成临时的 acessKey 还有其他信息,然后用这些信息来上传。

这种方案就是最完美的 OSS 上传方案了。

掌握了这些,就完全足够应对工作中的 OSS 使用了。

你可能感兴趣的:(web,前端)