亚马逊S3服务简单介绍

最近在做后端的开发,需要一些二进制数据保存在服务器云端。团队决定调查 AWS 的 S3 服务是否满足需求,所以就做了一些调查工作。不过也遇到很多坑的地方。所以记录下来,防止以后再遇到。

# 基本需求

需要云服务有稳定性保证,并且可以批量上传文件。可以设置上传和下载链接的有效期。

# AWS 试用

AWS 提供免费试用,但是注册的时候需要填写信用卡信息。确实有点不安,万一不小心被收费了就不好了。

# 生成上传凭证

我使用的是 AWS 的 JavaScript SDK (opens new window)。使用 createPresignedPost API 可以创建用于上传的凭证。这个凭证是根据用户的 AccessId,AccessKey 和 Policy 策略等计算生成的,并没有和 AWS 服务器直接进行交互。所以不用担心这个接口和 AWS 直接的流量费用问题。

var params = {
  Bucket: 'bucket',
  Conditions: [['starts-with', '$key', 'path/to/uploads/']]
};
s3.createPresignedPost(params, function(err, data) {
  if (err) {
    console.error('Presigning post data encountered an error', err);
  } else {
    data.Fields.key = 'path/to/uploads/${filename}';
    console.log('The post data is', data);
  }
});
1
2
3
4
5
6
7
8
9
10
11
12

官方提供的例子中,可以使用starts-with的方式来指定上传文件的 key 必须是以什么开头的,这样就可以指定上传的文件夹。很多文件也可以使用这一个上传凭证来完成上传。

# 上传 Policy 构造

AWS 提供了一个详细文档说明如何构造合法的 Policy:Creating a POST Policy (opens new window)。例如常见的需求就是在上传的时候添加 meta 信息声明文件的格式或者 MD5 值。 Policy 的 Conditions 数组里面可以添加["starts-with", "$x-amz-meta-md5checksum", ""]。最后一个参数为空字符串代表可以上传任何数值。

# 构造 POST 表单

AWS 也有文档说明了如何构造一个上传的表单 (opens new window)。这个表单中最重要的是一句注释:The elements after this will be ignored。在 file 字段之后的所有信息都会被忽略掉,我测试的时候一直把x-amz-meta-md5checksum字段放在 file 字段之后导致上传一直报错。直到 Stack Overflow 上面有人解释了才恍然大悟。

# 构造下载链接

我们使用getSignedUrl API 来生成下载链接,下载链接也是根据自己的 AccessId 和 AccessKey 生成链接的凭证,也没有和 AWS 服务器直接进行交互。当请求文件的时候,AWS 再计算凭证是否有效。所以后端无需和 AWS 交互就可以返回客户端有效的 AWS 下载链接。针对需要返回实际文件的 API 接口,可以采用返回 302 的跳转链接来完成需求。示例代码如下:

var params = { Bucket: 'bucket', Key: 'key' };
var url = s3.getSignedUrl('getObject', params);
console.log('The URL is', url);
1
2
3

可能有人会问,这个 API 也可以用来上传啊。但是这个 API 接口必须指定 key 值,这样我们就需要为每个文件来生成一个独立的上传 URL。这样太麻烦了。

# 总结

我们使用 pre-sign 的方式来生成 URL 主要是为了对客户端透明。虽然我们可以设置最小权限的 IAM User 给客户端,但是客户端很容易被逆向拿到敏感数据。这样难免会有风险,所以生成一个单纯的 URL 供客户端使用一定程度上保证了安全性也减少了客户端的复杂性。毕竟我也不想引入一个 AWS 的 SDK 进来。

前端开发地图应用的调研

我司的产品终于发布了新版本,所以忙碌的开发暂时告一段落。最近在做下一期的规划,然后就是要做前端网站来满足用户的需求。由于我们的产品是和地图强相关的,所以也对地图做了很多调研。

# 基本需求

产品需要在国内和国外使用,费用合理,可以换地图的贴图并且能在地图上绘制各种信息。例如多边形或圆形还有贝塞尔曲线。

# MapKit JS

苹果居然也出了网页版本的地图产品MapKit JS (opens new window),可谓是良心。但是目前还处于 Beta 版本。如果以后要考虑产品的全平台化,显然不是一个很好的选择。不过苹果的产品可以在国内国外使用不用太担心地图偏移问题,而且可以贴图和绘制图形,但是目前网页版还不能绘制贝塞尔曲线,iOS 客户端倒是可以。

# MapBox

MapBox 是一个地图服务提供商,经过调研 MapBox 使用最新的 WebGL 技术来渲染,摆脱了传统的下 tile 来贴图的方式,渲染速度得到了大大的提升。也能解决国内和国外的地图显示问题,但是画图功能就只有画线和多边形。不过提供了底层的画图层的接口,需要自己写 WebGL 的 shader。这就增加了开发成本。。。

# GoogleMap

谷歌地图当然好,但是我最后才说。因为谷歌无法在国内使用,这种情况下我就不得不写两套接口来对应,例如国内高德地图,国外谷歌地图,会大大增加开发成本。而且谷歌地图最近刚升级付费条款,费用是按照请求次数来收,所以用户大量增长之后的开销也会非常大。不过谷歌也不能画贝塞尔曲线。。。

# 地理坐标系

由于是需要在地图上绘制新的 tiles,所以自然就涉及到坐标的转换和计算。如何计算一个经纬度落在哪一张 tiles 上,以及在不同缩放级别下 tiles 的正常显示和重绘。这些都是需要自己来实现的。 这里有一个还算有名的官方介绍,并给出了 Python 的源码。可以参考Google Maps: Coordinates, Tile Bounds and Projection (opens new window)来进行实现。

# 坐标系转换

每个国家都有自己的坐标系系统,虽然 GPS 使用的WGS 84 (opens new window)标准非常流行,但是这个是美国制定的。每个国家当然都需要根据自己国家需要来定制自己的标准。例如中国就有北京 54 坐标系,西安 80 坐标系。北京 54 和西安 80 是参心坐标系,大地原点分别在苏联和西安。难以表达高度信息,目前国家正在推广2000 国家大地坐标系 (opens new window),这个和 WGS84 一样是地心坐标系,即以地球质量中心为原点。日本也有自己的平面直角坐标系,我们当然需要各种坐标转换,还好有现成的开源项目proj4js (opens new window)

# 总结

各个地图服务商都各有优劣,但却没有一款完美的。主要是用途也比较特殊,可能在地图服务上进行二次开发的可能性比较高吧。不过提供地图服务的也没几家可以选择的。。。

后端开发的技术总结

说实话,最近前端开发的事情并不是很多。所以自己也慢慢开始做后端开发,逐渐向全沾工程师靠拢。现在的开发工作,分工分明,后端也只需要专注 API 的请求与返回就好了。

# API 文档

使用API Blueprint (opens new window)作为 API 规范,并使用RSpec API Blueprint (opens new window)来自动生成文档。 生成的 Markdown 文件,可以很方便的转换成网页便于客户端开发。例如这个工具aglio (opens new window)

# 后端开发

现在采用的开发技术框架是 Ruby on Rails。不得不吐嘈一句,Ruby on Rails 在日本真是压倒性的流行。Ruby on Rails 还是 MVC 架构,原生支持 RESTful API。但我觉得下一代的接口标准应该是 facebook 推出的GraphQL (opens new window),不过现阶段 RESTful API 也足够了。

# Models

Models 主要是用来描述数据库结构,表与表之间的关系。还可以定义对数据库操作的方法,数据之间复杂的业务逻辑基本都是放在这里。

# Views

Views 主要是用来定义相应的数据的格式,把数据整合成统一的 JSON 标准供客户端使用。

# Controllers

Controllers 是请求的入口,用来定义请求的参数,并接收请求进行参数的过滤处理。然后调用 Models 里定义的方法完成数据的操作,并调用 Views 来完成数据的响应。

# 权限管理

权限管理是一件非常复杂的事情,定义某个接口在什么条件下可以调用,需要检查用户是什么身份。所以推荐是使用现有的成熟框架来进行管理。这里使用pundit (opens new window)来管理各个请求的权限检查。

# 软删除

在实际的项目中,我们需要进行软删除操作。例如用户加入了某个组织,以组织成员的身份发表了文章。当用户退出这个组织的时候,我们并不能直接删除数据库记录。否则无法显示文章的作者。这时候我们就需要软删除操作,对用户来说这条记录已经消失了。但对开发者来说,这条记录还是需要的。我们也使用框架来解决这个问题,这里采用的是Paranoia (opens new window)

# 测试

后端开发是偏向于纯逻辑的开发,所以测试非常重要。正常情况下的输入输出非常简单,代码的 80%都是需要考虑到各种各样的边界条件。需要考虑到用户的各种不同情况下的操作,给出合适的错误提示。所以需要完善的测试来覆盖所有场景,这里采用的是 e2e 测试,模拟请求,测试后端服务器的响应输出是否符合预期。采用的测试框架是rspec-rails (opens new window)

参考:

Ruby on Rails Guides (opens new window)