提升自我
VMware 提供培训和认证,助您加速提升。
了解更多这是六部分系列文章的第五部分,每周一和周四发布新文章,为 Spring 开发者介绍 Microsoft Azure。如果没有 Microsoft 的 Asir Vedamuthu Selvasingh、Yitao Dong、Bruno Borges、Brian Benz 和 Theresa Nguyen 的贡献,我无法完成本文。您可以在 Github 上找到本系列的代码。在阅读文章时,如有任何反馈或问题,请随时在 Twitter (@starbuxman) 上联系我。您还可以在我的 Spring Tips (@SpringTipsLive) 系列文章 Bootiful Azure 中了解更多关于 Microsoft Azure 的信息。
以下是所有文章
现在让我们来谈谈一些更... 日常的东西。理想情况下,你甚至不会经常去想它。应用程序通常有存储需求:它们可能需要存储用户上传的内容(如图片或文档等二进制数据),生成的工件,如 PDF 文件、视频、音乐等。它们可能想存储日志。不难想到应用程序可能想要持久存储的东西。
这些应用程序可以使用文件系统,例如本地机器上的文件系统,或者像 NFS(网络文件系统) 这样的网络附加文件系统。我将使用 NFS 泛指任何网络附加文件系统,例如 Samba、NFS 本身(那些使用 NFS 协议挂载的),或者像 DAC、FAL 等遗留选项。NFS 选项为文件提供类似文件系统的接口,通常采用分层格式。NFS 选项很有趣,因为它们类似于我们都熟悉的文件系统,而且每个人都知道如何使用文件系统,对吧?当然,一切都应该看起来像 Windows Explorer 中表示的层次结构?或者 macOS Finder?但实际上,它们彼此完全不同...
仔细想想,我说 NFS 选项像文件系统时,我不得不说“像”。有些支持比其他更细致的权限模型。有些支持对树中附加到文件和目录的元数据进行编码和复制。对不同操作的支持提供不同的速度保证;有些文件系统可能优化读取而非写入。有些可能优化目录遍历。客户端对文件读写的看法取决于所使用的客户端。(写入是否立即在所有复制节点上保持一致?)
即使我们假设所有我们要写入的文件系统都与 POSIX 兼容——你可以使用 int open(const char *path, int flags)
或 java.io.File
——但这可能不适用于我们的客户端。如果客户端不“懂”文件系统,而更喜欢以其他方式操作数据呢?如果客户端只能使用 HTTP 呢?或者如果它想使用 Bittorrent 通过对等网络更有效地整合下载呢?
由于这些以及更多原因,Amazon Web Services 推出了 S3,即简单云存储服务(懂了吗?“S”乘以 3?“S3”?),自那时起,它已成为所有其他云供应商都需要支持的普遍标准。对于 Microsoft Azure,对象存储服务 (OSS) 提供了类似 S3 的体验。它不是一个 POSIX 文件系统。您可以直接使用其 API,就像我们在此处所做的那样,但也可以使用 S3Proxy 使用 AWS S3 客户端代理写入 OSS,AWS S3 客户端有很多!Azure 的对象存储服务确实很无聊,这正是处理像文件状数据的持久卷这样基础的东西时你想要的。它甚至提供了一个名为 Microsoft Azure Storage Explorer 的独立浏览器,它可以在(没错!)Linux、Macintosh 和 Windows 上运行。这个独立浏览器让你能够查询 OSS 存储以及 CosmosDB 数据。这有多方便?你当然可以使用 az
CLI 或 API 本身。我们将使用 Spring 对 Java API 的集成。
您必须创建一个名为 bootiful
的 Azure 对象存储服务帐户,并将其分配给现有的资源组 bootiful
。然后,创建一个名为 files
的存储容器,并将其分配给刚刚创建的存储帐户。以下是一个示例脚本。
#!/usr/bin/env bash
rg=$1
accountname=bootiful
az storage account create --name ${accountname} --resource-group ${rg}
az storage container create -n files --account-name ${accountname}
使用以下命令确定连接应用程序所需的连接字符串。
az storage account show-connection-string --resource-group bootiful --name bootiful
记下连接字符串,以备后用。
将 com.microsoft.azure
: azure-storage-spring-boot-starter
添加到您的应用程序构建文件中。确保您已为 azure.storage.connection-string
属性指定了 OSS 连接字符串。
我们将读取应用程序 src/main/resources
目录中猫图片文件的字节,然后将这些字节作为“块 Blob”(原文中写为 block blog,应为 block blob)写入对象存储服务。您可以通过其他接口与 OSS 通信,但出于我们的目的,很自然地可以将其视为“容器”(逻辑分组,几乎就像目录或 S3 术语中的桶)和“Blob”的集合。Blob 本质上是一个文件,带有与其关联的名称和元数据。以下示例所做的就是将猫照片的字节存储到名为 files
的容器中,文件名为随机生成,前缀为 cat-
,后缀为 .jpg
。
package com.example.bootifulazure;
import com.microsoft.azure.storage.CloudStorageAccount;
import com.microsoft.azure.storage.StorageException;
import com.microsoft.azure.storage.blob.CloudBlobContainer;
import com.microsoft.azure.storage.blob.CloudBlockBlob;
import lombok.extern.log4j.Log4j2;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.event.ApplicationReadyEvent;
import org.springframework.context.event.EventListener;
import org.springframework.core.io.Resource;
import org.springframework.stereotype.Component;
import java.io.InputStream;
import java.net.URISyntaxException;
import java.util.UUID;
@Log4j2
@Component
class ObjectStorageServiceDemo {
private final CloudStorageAccount cloudStorageAccount;
private final Resource resource;
private final CloudBlobContainer files;
ObjectStorageServiceDemo(
CloudStorageAccount csa,
@Value("classpath:/cat.jpg") Resource cat) throws URISyntaxException, StorageException {
this.resource = cat;
this.cloudStorageAccount = csa;
this.files = this.cloudStorageAccount
.createCloudBlobClient()
.getContainerReference("files");
}
@EventListener(ApplicationReadyEvent.class)
public void demo() throws Exception {
CloudBlockBlob blockBlobReference = this.files.getBlockBlobReference("cat-" + UUID.randomUUID().toString() + ".jpg");
try (InputStream in = this.resource.getInputStream()) {
blockBlobReference.upload(in, this.resource.contentLength());
log.info("uploaded blockblob to " + blockBlobReference.getStorageUri());
}
}
}
Microsoft Azure 的特定部分并非微不足道。我们获取容器的引用,然后向其写入数据,接着输出资源的地址able URI。多么日常啊!这正是你在像文件系统这样的计算系统基础组件中想要的。它应该就是这么日常。老实说,我更高兴能够使用 Java 7 的 try-with-resources 语法来引用 Autocloseable
InputStream
!