Azure Cosmos DB 附件Azure Cosmos DB Attachments

适用于: SQL API

Azure Cosmos DB 附件是特殊项,它们包含对与外部 blob 或媒体文件关联的元数据的引用。Azure Cosmos DB attachments are special items that contain references to an associated metadata with an external blob or media file.

Azure Cosmos DB 支持两种类型的附件:Azure Cosmos DB supports two types of attachments:

  • 非托管附件 :是一个包装器,用于包装存储在外部服务(例如 Azure 存储、OneDrive 等)中的 blob 的 URI 引用。Unmanaged Attachments are a wrapper around an URI reference to a blob that is stored in an external service (for example, Azure Storage, OneDrive, etc.). 此方法类似于将 URI 属性存储在标准 Azure Cosmos DB 项中。This approach is similar to storing a URI property in a standard Azure Cosmos DB item.
  • 托管附件 :是由 Azure Cosmos DB 在内部管理并存储并通过系统生成的 mediaLink 公开的 blob。Managed Attachments are blobs managed and stored internally by Azure Cosmos DB and exposed via a system-generated mediaLink.

备注

附件是一项旧功能。Attachment is a legacy feature. 如果你已在使用此功能,则其支持范围限定为提供持续的功能。Their support is scoped to offer continued functionality if you are already using this feature.

建议使用 Azure Blob 存储作为专门的 blob 存储服务来存储 blob 数据,而不是使用附件。Instead of using attachments, we recommend you to use Azure Blob Storage as a purpose-built blob storage service to store blob data . 你可以继续将与 blob 相关的元数据连同引用 URI 链接一起作为项属性存储在 Azure Cosmos DB 中。You can continue to store metadata related to blobs, along with reference URI links, in Azure Cosmos DB as item properties. 将此数据存储在 Azure Cosmos DB 中可以查询元数据并链接到存储在 Azure Blob 存储中的 blob。Storing this data in Azure Cosmos DB provides the ability to query metadata and links to blobs stored in Azure Blob Storage.

Azure 承诺在完全弃用附件之前提供最少 36 个月的通知,这将在以后公布。Azure is committed to provide a minimum 36-month notice prior to fully deprecating attachments - which will be announced at a further date.

已知的限制Known limitations

Azure Cosmos DB 的托管附件不同于其对标准项的支持 - 对于标准项,它提供无限制的可伸缩性、多区域分布以及与其他 Azure 服务的集成。Azure Cosmos DB's managed attachments are distinct from its support for standard items - for which it offers unlimited scalability, multiple-region distribution, and integration with other Azure services.

  • 并非所有版本的 Azure Cosmos DB SDK 都支持附件。Attachments aren't supported in all versions of the Azure Cosmos DB's SDKs.
  • 每个数据库帐户的托管附件限制为 2 GB 存储。Managed attachments are limited to 2 GB of storage per database account.
  • 托管附件与 Azure Cosmos DB 的多区域分布不兼容,它们不会跨区域复制。Managed attachments aren't compatible with Azure Cosmos DB's multiple-region distribution, and they aren't replicated across regions.

将附件迁移到 Azure Blob 存储Migrating Attachments to Azure Blob Storage

建议通过以下步骤将 Azure Cosmos DB 附件迁移到 Azure Blob 存储:We recommend migrating Azure Cosmos DB attachments to Azure Blob Storage by following these steps:

  1. 将源 Azure Cosmos DB 容器中的附件数据复制到目标 Azure Blob 存储容器。Copy attachments data from your source Azure Cosmos DB container to your target Azure Blob Storage container.
  2. 验证目标 Azure Blob 存储容器中已上传的 blob 数据。Validate the uploaded blob data in the target Azure Blob Storage container.
  3. 如果适用,将对 Azure Blob 存储中包含的 blob 的 URI 引用添加为 Azure Cosmos DB 数据集中的字符串属性。If applicable, add URI references to the blobs contained in Azure Blob Storage as string properties within your Azure Cosmos DB dataset.
  4. 重构你的应用程序代码,以便从新的 Azure Blob 存储容器读取和写入 blob。Refactor your application code to read and write blobs from the new Azure Blob Storage container.

以下 dotnet 代码示例展示了在完成迁移流的过程中,如何使用 Azure Cosmos DB 的 .NET SDK v2 和 Azure Blob 存储 .NET SDK v12 将附件从 Azure Cosmos DB 复制到 Azure Blob 存储。The following dotnet code sample shows how to copy attachments from Azure Cosmos DB to Azure Blob storage as part of a migration flow by using Azure Cosmos DB's .NET SDK v2 and Azure Blob Storage .NET SDK v12. 请确保为源 Azure Cosmos DB 帐户和目标 Azure Blob 存储容器替换 <placeholder values>Make sure to replace the <placeholder values> for the source Azure Cosmos DB account and target Azure Blob storage container.


using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.Azure.Documents;
using Microsoft.Azure.Documents.Client;
using Azure.Storage.Blobs;
using Azure.Storage.Blobs.Models;

namespace attachments
{
    class Program
    {
        private static string cosmosAccount = "<Your_Azure_Cosmos_account_URI>";
        private static string cosmosKey = "<Your_Azure_Cosmos_account_PRIMARY_KEY>";
        private static string cosmosDatabaseName = "<Your_Azure_Cosmos_database>";
        private static string cosmosCollectionName = "<Your_Azure_Cosmos_collection>";
        private static string storageConnectionString = "<Your_Azure_Storage_connection_string>";
        private static string storageContainerName = "<Your_Azure_Storage_container_name>";
        private static DocumentClient cosmosClient = new DocumentClient(new Uri(cosmosAccount), cosmosKey);
        private static BlobServiceClient storageClient = new BlobServiceClient(storageConnectionString);
        private static BlobContainerClient storageContainerClient = storageClient.GetBlobContainerClient(storageContainerName);

        static void Main(string[] args)
        {
            CopyAttachmentsToBlobsAsync().Wait();
        }

        private async static Task CopyAttachmentsToBlobsAsync()
        {
            Console.WriteLine("Copying Azure Cosmos DB Attachments to Azure Blob Storage ...");

            int totalCount = 0;
            string docContinuation = null;

            // Iterate through each item (document in v2) in the Azure Cosmos DB container (collection in v2) to look for attachments.
            do
            {
                FeedResponse<dynamic> response = await cosmosClient.ReadDocumentFeedAsync(
                    UriFactory.CreateDocumentCollectionUri(cosmosDatabaseName, cosmosCollectionName),
                    new FeedOptions
                    {
                        MaxItemCount = -1,
                        RequestContinuation = docContinuation
                    });
                docContinuation = response.ResponseContinuation;

                foreach (Document document in response)
                {
                    string attachmentContinuation = null;
                    PartitionKey docPartitionKey = new PartitionKey(document.Id);

                    // Iterate through each attachment within the item (if any).
                    do
                    {
                        FeedResponse<Attachment> attachments = await cosmosClient.ReadAttachmentFeedAsync(
                            document.SelfLink,
                            new FeedOptions
                            {
                                PartitionKey = docPartitionKey,
                                RequestContinuation = attachmentContinuation
                            }
                        );
                        attachmentContinuation = attachments.ResponseContinuation;

                        foreach (var attachment in attachments)
                        {
                            // Download the attachment in to local memory.
                            MediaResponse content = await cosmosClient.ReadMediaAsync(attachment.MediaLink);

                            byte[] buffer = new byte[content.ContentLength];
                            await content.Media.ReadAsync(buffer, 0, buffer.Length);

                            // Upload the locally buffered attachment to blob storage
                            string blobId = String.Concat(document.Id, "-", attachment.Id);

                            Azure.Response<BlobContentInfo> uploadedBob = await storageContainerClient.GetBlobClient(blobId).UploadAsync(
                                new MemoryStream(buffer, writable: false),
                                true
                            );

                            Console.WriteLine("Copied attachment ... Item Id: {0} , Attachment Id: {1}, Blob Id: {2}", document.Id, attachment.Id, blobId);
                            totalCount++;

                            // Clean up attachment from Azure Cosmos DB.
                            // Warning: please verify you've succesfully migrated attachments to blog storage prior to cleaning up Azure Cosmos DB.
                            // await cosmosClient.DeleteAttachmentAsync(
                            //     attachment.SelfLink,
                            //     new RequestOptions { PartitionKey = docPartitionKey }
                            // );

                            // Console.WriteLine("Cleaned up attachment ... Document Id: {0} , Attachment Id: {1}", document.Id, attachment.Id);
                        }

                    } while (!string.IsNullOrEmpty(attachmentContinuation));
                }
            }
            while (!string.IsNullOrEmpty(docContinuation));

            Console.WriteLine("Finished copying {0} attachments to blob storage", totalCount);
        }
    }
}

后续步骤Next steps