Amazon S3 是一种简单的文件存储解决方案,非常适合存储内容,但当用作基于 Web 的文件浏览器的存储机制时,它的效果如何?
最近我的任务就是为客户做这件事。此外,与现有解决方案(使用 CKFinder 并在我们自己的服务器和存储桶之间同步文件副本)相反,我需要直接连接到 S3 存储桶。在这篇文章中,我将讨论我们是如何做到的。
不要重新发明轮子……或者也许我们应该这样做!
最初,最好的办法似乎是使用现有的连接到 S3 的基于 Web 的文件资源管理器的实现。我认为一定有人花时间实施了一个好的解决方案。所以我在网上看了看,发现了 2 个可能的竞争者,CKFinder with an S3 plugin 和 ELFinder with an S3 plugin 。它们都是用 PHP 编写的,这并不理想,但我们可以接受。
我决定先试用 ELFinder 看看它的性能如何。我开始上传一些文件和文件夹,效果很好,所以我决定看看它在存储桶中有多个文件时表现如何。
为此,我连接到另一个存储桶,该存储桶具有我们计划使用的实时环境的副本。它包含数以万计的文件和大约 2,600 个不同的目录。性能真的很糟糕——加载需要将近 1 分钟,而完成任何类型的操作(例如导航)大约需要 1 分钟。
那么为什么这么慢?
由于实现方式 和 S3 的工作方式,ELFinder 确实很慢。
S3 与传统文件系统 不同 。它适用于键/值对模型。文件的路径(包括文件名)构成键,值是文件的内容。
您可以根据前缀搜索文件,这为显示目录内容提供了合理的性能。但是,S3 没有提供列出目录结构的有效方法。
因此,为了甚至列出顶级目录,您需要从 S3 请求 所有 键,遍历它们并根据每个键的前缀计算出顶级目录名称。这使得 ELFinder 非常慢,因为它会在每个请求上执行此操作。
坚持基础并做好
因为 CKFinder 的 S3 插件也有问题,项目技术负责人 Pablo Caif 和我选择编写我们自己的实现,使用 Java/ Spring Boot 后端和 Angular 前端。
我们在后端创建了一个 RESTful Web 服务,缓存了 S3 中的所有密钥。使用这些键,我们能够构建内存中的树结构,以便我们可以快速检索目录中的文件夹列表。我们还创建了一个内存中的哈希映射,其中包含每个目录的文件列表,以便快速检索。
为了降低项目的复杂性,我们选择采用 最小可行产品 (MVP) 方法。这减少了实施需要额外时间来实施和支持的功能的需要。
我们支持导航、删除文件、上传文件、创建文件夹、重命名文件、下载文件和删除文件夹(但仅当文件夹为空时)。关于删除文件夹的最后警告有助于降低复杂性,并可防止意外删除文件夹的全部内容。
我们不需要重命名文件夹的功能。这是幸运的,因为 S3 不允许文件重命名。我们会要求程序遍历目标文件夹中的所有文件并制作它们的副本,并在密钥中使用新的目录名称。这将是一个缓慢的过程,因为我们可能不得不复制数 GB 的数据来重命名文件夹。
清空文件夹,容易吗?
支持空文件夹并不简单,因为 S3 本身不支持它们。由于文件的整个路径都存储在其名称中,因此没有创建“空”文件夹的好方法。
我们最终通过使用一个隐藏文件和一个表示文件夹为空的特定键来实现空文件夹。这工作得很好,除了不是由我们的应用程序创建的目录——一旦我们的内存缓存更新,空文件夹就会消失,而无需用户删除它。因此,我们的实施假定 S3 存储桶中会满足一些预先存在的条件,以便一切正常运行。
搜索
我们还必须提供一种在给定目录中搜索文件的方法。这绝对是使用S3做不到的事情。由于只能根据前缀搜索 S3 存储桶,因此我们需要实现自己的搜索功能。
值得庆幸的是,拥有文件缓存和使用 AngularJS 过滤器使得搜索文件的过程实现起来相当简单,同时提供了快速的用户体验。但是,这确实意味着我们将目录中的完整文件列表发送给用户,以便他们可以过滤该目录中的文件。幸运的是,在我们的测试过程中,我们没有注意到任何性能下降。
表现
我们的文件系统实施正在形成,比任何其他可用的都快得多。我们将所有文件结构缓存在内存中,这样当用户加载应用程序或浏览目录时,就无需向 S3 发出请求。
应用程序每 5 分钟刷新一次缓存,该过程大约需要 15-20 秒。这比为每个请求都这样做要好得多。唯一需要访问 S3 存储桶的时间是在缓存刷新期间。
可用于 PC/Mac 的现有 S3 文件系统产品的另一个问题是它们使用 AWS 凭证进行身份验证,这将防止我们的业务用户面临安全风险。通过我们的实施,我们能够将 AWS IAM 角色保留在服务器上,并为每个用户提供登录应用程序的凭据。
我们还收到了限制对某些目录的读取和/或写入访问的延迟要求。这可以使用 Spring Security 来实现,以防止目录显示给用户并限制对某些文件的写访问。我能找到的任何可用的 S3 文件资源管理器都不支持此功能。
缓存缩略图
我们还为文件资源管理器提供了 2 种显示模式。一个只会列出文件,另一个会显示图像的缩略图。在实现此功能时,S3 为文件提供的哈希码实际上非常有用,可确保我们不会在每次请求图像时都下载并生成图像缩略图。当请求缩略图时,图像将从 S3 下载到 Web 服务器。
使用 ImgScalr Java 库减小了文件的大小,并将结果与哈希码一起保存在网络服务器上。当再次请求相同的缩略图时,应用程序只需从 S3 获取文件的哈希码,将其与上一次请求的哈希码进行比较,如果匹配,缓存的缩略图将返回给用户。
结论
Amazon S3 提供高度冗余、高度可用和高度可扩展的存储服务。但是,重要的是要记住它不是基于目录的文件系统。相反,它将文件存储在没有真正目录功能的键值对中。
在我们的实例中,S3 表现出某种阻抗不匹配,因此在实施我们的解决方案时,我们必须跳出框框思考,以创建一个快速可靠的文件浏览器。我真的很喜欢实施这个解决方案并解决出现的各种问题。最终结果为客户提供了很多价值,我们已经能够提供性能优于大多数其他解决方案的解决方案。