深入解析阿里云对象存储服务(OSS、Node)

2,232 阅读5分钟

对象存储服务(OSS)介绍

文件上传是常见的网络需求,通常我们不会将文件直接上传到应用服务器,因为单台服务器的存储空间有限,不易扩展。
为此,我们通常使用对象存储服务(Object Storage Service,简称OSS)来完成文件的上传和下载。例如,阿里云提供的 OSS 服务就是这样一个平台。
OSS 存储和检索非结构化数据和元数据对象(如文档、图片、视频等)。

本地存储和 OSS 存储结构对比

本地文件存储:采用目录和文件的组织方式。
OSS 存储结构:

  • OSS 中的对象是存储的基本单元,每个对象包含数据、元数据和唯一标识符。
  • 桶(Bucket)是用于组织对象的容器,每个桶内可以存储无数个对象,并且可以通过 RESTful API 接口进行操作。
  • 值得注意的是,虽然阿里云 OSS 的控制台显示对象存储没有目录层级结构,但它实际上是通过元数据部分模拟实现了目录层级,使得用户可以使用“标签”来检索文件。

阿里云的其他存储服务

除了对象存储OSS,阿里云还提供文件存储和块存储服务。
文件存储具有目录层次结构,可以上传和下载文件,但存储容量有限。
块存储则是将整块磁盘提供给你,需要自行格式化,存储容量同样有限。
对象存储OSS则是分布式实现的 key-value 存储,存储容量几乎是无限的。
绝大多数情况下,我们都是用 OSS 对象存储。

OSS 服务购买

我们买个阿里云的 OSS 服务
image.png
我买了 40G 的 OSS 国内通用资源包,花了 5 块钱。

然后我们创建个 Bucket(桶):
image.png
在北京创建了一个 Bucket,文件就会存储在那里的服务器上。
可以设置为公共读,也可以设置为私有,需要身份验证才能访问。
如果文件仅存储在北京的服务器上,那么在其他地区访问是否会受到速度的影响?
这就是 CDN(内容分发网络)的用武之地。通过 CDN,用户访问特定域名时,会被引导至最近的缓存服务器,从而加速文件的访问速度。

控制台上传文件

创建 Bucket 之后,我们就可以上传文件了:
image.png
image.png
image.png

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

生产环境下我们不会直接用 OSS 的 URL 访问,而是会开启 CDN:

  • 当用户通过网站域名请求文件时,请求会被 CDN 服务接收。
  • CDN 会根据用户的地理位置,将请求重定向到最近的缓存服务器。
  • 如果这个服务器上已经缓存了请求的文件,那么文件将直接从该服务器提供给用户,从而减少了数据传输的延迟。
  • 如果缓存服务器上没有文件,它会从原始位置(即 OSS 服务)获取文件,并将其缓存以备后续请求使用。

Node 上传下载 OSS 图片

代码准备

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

安装用到的包:

npm install ali-oss

代码:

const OSS = require('ali-oss')

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

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

put();

new OSS 的时候有几个选项需要获取。

获取 region

region 在概览可以看到:
image.png

获取 accessKey

这里的 accessKeyId 和 acessKeySecret 是什么呢?
阿里云的安全策略建议使用 AccessKey(包括 AccessKeyId 和 AccessKeySecret )进行身份认证,而不是直接使用用户名和密码。
为了进一步减少安全风险,建议使用 RAM(Resource Access Management)子用户的方式生成 AccessKey,这样可以最小化权限。
我们创建个 accesKey:
image.png
image.png
image.png
image.png
image.png
创建完成后,就可以拿到 accesKeyId 和 accessKeySecret。

设置访问控制

image.png
image.png
新增一个授权:
image.png
把 OSS 的管理和读取权限给这个子用户:
image.png

运行上传文件代码

node 运行下:
image.png
这时候就上传成功了。
阿里云的大文件分片上传直接看文档就行了。

下载图片

image.png
image.png

OSS 上传方案

在实际应用中,我们可以选择让客户端直接将文件上传至 OSS:

也可以先上传至应用服务器,再由应用服务器转发至 OSS。

直接上传可以节省应用服务器的流量,但增加了 accessKey 泄露的风险

有没有什么两全其美的办法?
image.png
阿里云的文档里也提到了这个问题。
它给出的解决方案就是生成一个临时的签名来用。
代码是这样的:

const OSS = require('ali-oss');

async function main() {
	const config = {
		region: '',
		bucket: '',
		accessKeyId: '',
		accessKeySecret: '',
	};

	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();

获取到了临时凭证的信息:
image.png
这样就能在网页里用这些来上传文件到 OSS 了:
创建个 index.html:

<!DOCTYPE html>
<html lang="en">
	<head>
		<script src="https://unpkg.com/axios@1.6.5/dist/axios.min.js"></script>
	</head>
	<body>
		<input id="fileInput" type="file" />

		<script>
			const fileInput = document.getElementById('fileInput');

			async function getOSSInfo() {
				// 下面的信息可以通过请求服务器获取
				return {
					OSSAccessKeyId: '',
					Signature: '',
					policy: '',
					host: '',
				};
			}

			fileInput.onchange = async () => {
				const file = fileInput.files[0];

				const ossInfo = await getOSSInfo();

				const formdata = new FormData();

				formdata.append('key', file.name);
				formdata.append('OSSAccessKeyId', ossInfo.OSSAccessKeyId);
				formdata.append('policy', ossInfo.policy);
				formdata.append('signature', ossInfo.Signature);
				formdata.append('success_action_status', '200');
				formdata.append('file', file);

				const res = await axios.post(ossInfo.host, formdata);
				if (res.status === 200) {
					const img = document.createElement('img');
					img.src = ossInfo.host + '/' + file.name;
					document.body.append(img);

					alert('上传成功');
				}
			};
		</script>
	</body>
</html>

跑个静态服务器:

npx http-server .

我们需要在控制台开启下跨域:
image.png
上传文件:
image.png
上传成功了。
控制台文件列表也可以看到这个文件:
image.png
这就是完美的 OSS 上传方案。

服务端用 RAM 子用户的 accessKey 来生成临时签名,然后返回给客户端,客户端用这个来直传文件到 OSS。
因为临时的签名过期时间很短,我们设置的是一天,所以暴露的风险也不大。
这样服务端就根本没有接受文件的压力,只要等客户端上传完之后,带上 URL 就好了。