@azure/storage-blob
Version:
Microsoft Azure Storage SDK for JavaScript - Blob
1,093 lines (1,092 loc) • 60.2 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.ContainerClient = void 0;
const core_rest_pipeline_1 = require("@azure/core-rest-pipeline");
const core_util_1 = require("@azure/core-util");
const core_auth_1 = require("@azure/core-auth");
const AnonymousCredential_js_1 = require("./credentials/AnonymousCredential.js");
const StorageSharedKeyCredential_js_1 = require("./credentials/StorageSharedKeyCredential.js");
const Pipeline_js_1 = require("./Pipeline.js");
const StorageClient_js_1 = require("./StorageClient.js");
const tracing_js_1 = require("./utils/tracing.js");
const utils_common_js_1 = require("./utils/utils.common.js");
const BlobSASSignatureValues_js_1 = require("./sas/BlobSASSignatureValues.js");
const BlobLeaseClient_js_1 = require("./BlobLeaseClient.js");
const Clients_js_1 = require("./Clients.js");
const BlobBatchClient_js_1 = require("./BlobBatchClient.js");
/**
* A ContainerClient represents a URL to the Azure Storage container allowing you to manipulate its blobs.
*/
class ContainerClient extends StorageClient_js_1.StorageClient {
/**
* containerContext provided by protocol layer.
*/
containerContext;
_containerName;
/**
* The name of the container.
*/
get containerName() {
return this._containerName;
}
constructor(urlOrConnectionString, credentialOrPipelineOrContainerName,
// Legacy, no fix for eslint error without breaking. Disable it for this interface.
/* eslint-disable-next-line @azure/azure-sdk/ts-naming-options*/
options) {
let pipeline;
let url;
options = options || {};
if ((0, Pipeline_js_1.isPipelineLike)(credentialOrPipelineOrContainerName)) {
// (url: string, pipeline: Pipeline)
url = urlOrConnectionString;
pipeline = credentialOrPipelineOrContainerName;
}
else if ((core_util_1.isNodeLike && credentialOrPipelineOrContainerName instanceof StorageSharedKeyCredential_js_1.StorageSharedKeyCredential) ||
credentialOrPipelineOrContainerName instanceof AnonymousCredential_js_1.AnonymousCredential ||
(0, core_auth_1.isTokenCredential)(credentialOrPipelineOrContainerName)) {
// (url: string, credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential, options?: StoragePipelineOptions)
url = urlOrConnectionString;
pipeline = (0, Pipeline_js_1.newPipeline)(credentialOrPipelineOrContainerName, options);
}
else if (!credentialOrPipelineOrContainerName &&
typeof credentialOrPipelineOrContainerName !== "string") {
// (url: string, credential?: StorageSharedKeyCredential | AnonymousCredential | TokenCredential, options?: StoragePipelineOptions)
// The second parameter is undefined. Use anonymous credential.
url = urlOrConnectionString;
pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options);
}
else if (credentialOrPipelineOrContainerName &&
typeof credentialOrPipelineOrContainerName === "string") {
// (connectionString: string, containerName: string, blobName: string, options?: StoragePipelineOptions)
const containerName = credentialOrPipelineOrContainerName;
const extractedCreds = (0, utils_common_js_1.extractConnectionStringParts)(urlOrConnectionString);
if (extractedCreds.kind === "AccountConnString") {
if (core_util_1.isNodeLike) {
const sharedKeyCredential = new StorageSharedKeyCredential_js_1.StorageSharedKeyCredential(extractedCreds.accountName, extractedCreds.accountKey);
url = (0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName));
if (!options.proxyOptions) {
options.proxyOptions = (0, core_rest_pipeline_1.getDefaultProxySettings)(extractedCreds.proxyUri);
}
pipeline = (0, Pipeline_js_1.newPipeline)(sharedKeyCredential, options);
}
else {
throw new Error("Account connection string is only supported in Node.js environment");
}
}
else if (extractedCreds.kind === "SASConnString") {
url =
(0, utils_common_js_1.appendToURLPath)(extractedCreds.url, encodeURIComponent(containerName)) +
"?" +
extractedCreds.accountSas;
pipeline = (0, Pipeline_js_1.newPipeline)(new AnonymousCredential_js_1.AnonymousCredential(), options);
}
else {
throw new Error("Connection string must be either an Account connection string or a SAS connection string");
}
}
else {
throw new Error("Expecting non-empty strings for containerName parameter");
}
super(url, pipeline);
this._containerName = this.getContainerNameFromUrl();
this.containerContext = this.storageClientContext.container;
}
/**
* Creates a new container under the specified account. If the container with
* the same name already exists, the operation fails.
* @see https://learn.microsoft.com/rest/api/storageservices/create-container
* Naming rules: @see https://learn.microsoft.com/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
*
* @param options - Options to Container Create operation.
*
*
* Example usage:
*
* ```ts snippet:ContainerClientCreate
* import { BlobServiceClient } from "@azure/storage-blob";
* import { DefaultAzureCredential } from "@azure/identity";
*
* const account = "<account>";
* const blobServiceClient = new BlobServiceClient(
* `https://${account}.blob.core.windows.net`,
* new DefaultAzureCredential(),
* );
*
* const containerName = "<container name>";
* const containerClient = blobServiceClient.getContainerClient(containerName);
* const createContainerResponse = await containerClient.create();
* console.log("Container was created successfully", createContainerResponse.requestId);
* ```
*/
async create(options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-create", options, async (updatedOptions) => {
return (0, utils_common_js_1.assertResponse)(await this.containerContext.create(updatedOptions));
});
}
/**
* Creates a new container under the specified account. If the container with
* the same name already exists, it is not changed.
* @see https://learn.microsoft.com/rest/api/storageservices/create-container
* Naming rules: @see https://learn.microsoft.com/rest/api/storageservices/naming-and-referencing-containers--blobs--and-metadata
*
* @param options -
*/
async createIfNotExists(options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-createIfNotExists", options, async (updatedOptions) => {
try {
const res = await this.create(updatedOptions);
return {
succeeded: true,
...res,
_response: res._response, // _response is made non-enumerable
};
}
catch (e) {
if (e.details?.errorCode === "ContainerAlreadyExists") {
return {
succeeded: false,
...e.response?.parsedHeaders,
_response: e.response,
};
}
else {
throw e;
}
}
});
}
/**
* Returns true if the Azure container resource represented by this client exists; false otherwise.
*
* NOTE: use this function with care since an existing container might be deleted by other clients or
* applications. Vice versa new containers with the same name might be added by other clients or
* applications after this function completes.
*
* @param options -
*/
async exists(options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-exists", options, async (updatedOptions) => {
try {
await this.getProperties({
abortSignal: options.abortSignal,
tracingOptions: updatedOptions.tracingOptions,
});
return true;
}
catch (e) {
if (e.statusCode === 404) {
return false;
}
throw e;
}
});
}
/**
* Creates a {@link BlobClient}
*
* @param blobName - A blob name
* @returns A new BlobClient object for the given blob name.
*/
getBlobClient(blobName) {
return new Clients_js_1.BlobClient((0, utils_common_js_1.appendToURLPath)(this.url, (0, utils_common_js_1.EscapePath)(blobName)), this.pipeline);
}
/**
* Creates an {@link AppendBlobClient}
*
* @param blobName - An append blob name
*/
getAppendBlobClient(blobName) {
return new Clients_js_1.AppendBlobClient((0, utils_common_js_1.appendToURLPath)(this.url, (0, utils_common_js_1.EscapePath)(blobName)), this.pipeline);
}
/**
* Creates a {@link BlockBlobClient}
*
* @param blobName - A block blob name
*
*
* Example usage:
*
* ```ts snippet:ClientsUpload
* import { BlobServiceClient } from "@azure/storage-blob";
* import { DefaultAzureCredential } from "@azure/identity";
*
* const account = "<account>";
* const blobServiceClient = new BlobServiceClient(
* `https://${account}.blob.core.windows.net`,
* new DefaultAzureCredential(),
* );
*
* const containerName = "<container name>";
* const blobName = "<blob name>";
* const containerClient = blobServiceClient.getContainerClient(containerName);
* const blockBlobClient = containerClient.getBlockBlobClient(blobName);
*
* const content = "Hello world!";
* const uploadBlobResponse = await blockBlobClient.upload(content, content.length);
* ```
*/
getBlockBlobClient(blobName) {
return new Clients_js_1.BlockBlobClient((0, utils_common_js_1.appendToURLPath)(this.url, (0, utils_common_js_1.EscapePath)(blobName)), this.pipeline);
}
/**
* Creates a {@link PageBlobClient}
*
* @param blobName - A page blob name
*/
getPageBlobClient(blobName) {
return new Clients_js_1.PageBlobClient((0, utils_common_js_1.appendToURLPath)(this.url, (0, utils_common_js_1.EscapePath)(blobName)), this.pipeline);
}
/**
* Returns all user-defined metadata and system properties for the specified
* container. The data returned does not include the container's list of blobs.
* @see https://learn.microsoft.com/rest/api/storageservices/get-container-properties
*
* WARNING: The `metadata` object returned in the response will have its keys in lowercase, even if
* they originally contained uppercase characters. This differs from the metadata keys returned by
* the `listContainers` method of {@link BlobServiceClient} using the `includeMetadata` option, which
* will retain their original casing.
*
* @param options - Options to Container Get Properties operation.
*/
async getProperties(options = {}) {
if (!options.conditions) {
options.conditions = {};
}
return tracing_js_1.tracingClient.withSpan("ContainerClient-getProperties", options, async (updatedOptions) => {
return (0, utils_common_js_1.assertResponse)(await this.containerContext.getProperties({
abortSignal: options.abortSignal,
...options.conditions,
tracingOptions: updatedOptions.tracingOptions,
}));
});
}
/**
* Marks the specified container for deletion. The container and any blobs
* contained within it are later deleted during garbage collection.
* @see https://learn.microsoft.com/rest/api/storageservices/delete-container
*
* @param options - Options to Container Delete operation.
*/
async delete(options = {}) {
if (!options.conditions) {
options.conditions = {};
}
return tracing_js_1.tracingClient.withSpan("ContainerClient-delete", options, async (updatedOptions) => {
return (0, utils_common_js_1.assertResponse)(await this.containerContext.delete({
abortSignal: options.abortSignal,
leaseAccessConditions: options.conditions,
modifiedAccessConditions: options.conditions,
tracingOptions: updatedOptions.tracingOptions,
}));
});
}
/**
* Marks the specified container for deletion if it exists. The container and any blobs
* contained within it are later deleted during garbage collection.
* @see https://learn.microsoft.com/rest/api/storageservices/delete-container
*
* @param options - Options to Container Delete operation.
*/
async deleteIfExists(options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-deleteIfExists", options, async (updatedOptions) => {
try {
const res = await this.delete(updatedOptions);
return {
succeeded: true,
...res,
_response: res._response,
};
}
catch (e) {
if (e.details?.errorCode === "ContainerNotFound") {
return {
succeeded: false,
...e.response?.parsedHeaders,
_response: e.response,
};
}
throw e;
}
});
}
/**
* Sets one or more user-defined name-value pairs for the specified container.
*
* If no option provided, or no metadata defined in the parameter, the container
* metadata will be removed.
*
* @see https://learn.microsoft.com/rest/api/storageservices/set-container-metadata
*
* @param metadata - Replace existing metadata with this value.
* If no value provided the existing metadata will be removed.
* @param options - Options to Container Set Metadata operation.
*/
async setMetadata(metadata, options = {}) {
if (!options.conditions) {
options.conditions = {};
}
if (options.conditions.ifUnmodifiedSince) {
throw new RangeError("the IfUnmodifiedSince must have their default values because they are ignored by the blob service");
}
return tracing_js_1.tracingClient.withSpan("ContainerClient-setMetadata", options, async (updatedOptions) => {
return (0, utils_common_js_1.assertResponse)(await this.containerContext.setMetadata({
abortSignal: options.abortSignal,
leaseAccessConditions: options.conditions,
metadata,
modifiedAccessConditions: options.conditions,
tracingOptions: updatedOptions.tracingOptions,
}));
});
}
/**
* Gets the permissions for the specified container. The permissions indicate
* whether container data may be accessed publicly.
*
* WARNING: JavaScript Date will potentially lose precision when parsing startsOn and expiresOn strings.
* For example, new Date("2018-12-31T03:44:23.8827891Z").toISOString() will get "2018-12-31T03:44:23.882Z".
*
* @see https://learn.microsoft.com/rest/api/storageservices/get-container-acl
*
* @param options - Options to Container Get Access Policy operation.
*/
async getAccessPolicy(options = {}) {
if (!options.conditions) {
options.conditions = {};
}
return tracing_js_1.tracingClient.withSpan("ContainerClient-getAccessPolicy", options, async (updatedOptions) => {
const response = (0, utils_common_js_1.assertResponse)(await this.containerContext.getAccessPolicy({
abortSignal: options.abortSignal,
leaseAccessConditions: options.conditions,
tracingOptions: updatedOptions.tracingOptions,
}));
const res = {
_response: response._response,
blobPublicAccess: response.blobPublicAccess,
date: response.date,
etag: response.etag,
errorCode: response.errorCode,
lastModified: response.lastModified,
requestId: response.requestId,
clientRequestId: response.clientRequestId,
signedIdentifiers: [],
version: response.version,
};
for (const identifier of response) {
let accessPolicy = undefined;
if (identifier.accessPolicy) {
accessPolicy = {
permissions: identifier.accessPolicy.permissions,
};
if (identifier.accessPolicy.expiresOn) {
accessPolicy.expiresOn = new Date(identifier.accessPolicy.expiresOn);
}
if (identifier.accessPolicy.startsOn) {
accessPolicy.startsOn = new Date(identifier.accessPolicy.startsOn);
}
}
res.signedIdentifiers.push({
accessPolicy,
id: identifier.id,
});
}
return res;
});
}
/**
* Sets the permissions for the specified container. The permissions indicate
* whether blobs in a container may be accessed publicly.
*
* When you set permissions for a container, the existing permissions are replaced.
* If no access or containerAcl provided, the existing container ACL will be
* removed.
*
* When you establish a stored access policy on a container, it may take up to 30 seconds to take effect.
* During this interval, a shared access signature that is associated with the stored access policy will
* fail with status code 403 (Forbidden), until the access policy becomes active.
* @see https://learn.microsoft.com/rest/api/storageservices/set-container-acl
*
* @param access - The level of public access to data in the container.
* @param containerAcl - Array of elements each having a unique Id and details of the access policy.
* @param options - Options to Container Set Access Policy operation.
*/
async setAccessPolicy(access, containerAcl, options = {}) {
options.conditions = options.conditions || {};
return tracing_js_1.tracingClient.withSpan("ContainerClient-setAccessPolicy", options, async (updatedOptions) => {
const acl = [];
for (const identifier of containerAcl || []) {
acl.push({
accessPolicy: {
expiresOn: identifier.accessPolicy.expiresOn
? (0, utils_common_js_1.truncatedISO8061Date)(identifier.accessPolicy.expiresOn)
: "",
permissions: identifier.accessPolicy.permissions,
startsOn: identifier.accessPolicy.startsOn
? (0, utils_common_js_1.truncatedISO8061Date)(identifier.accessPolicy.startsOn)
: "",
},
id: identifier.id,
});
}
return (0, utils_common_js_1.assertResponse)(await this.containerContext.setAccessPolicy({
abortSignal: options.abortSignal,
access,
containerAcl: acl,
leaseAccessConditions: options.conditions,
modifiedAccessConditions: options.conditions,
tracingOptions: updatedOptions.tracingOptions,
}));
});
}
/**
* Get a {@link BlobLeaseClient} that manages leases on the container.
*
* @param proposeLeaseId - Initial proposed lease Id.
* @returns A new BlobLeaseClient object for managing leases on the container.
*/
getBlobLeaseClient(proposeLeaseId) {
return new BlobLeaseClient_js_1.BlobLeaseClient(this, proposeLeaseId);
}
/**
* Creates a new block blob, or updates the content of an existing block blob.
*
* Updating an existing block blob overwrites any existing metadata on the blob.
* Partial updates are not supported; the content of the existing blob is
* overwritten with the new content. To perform a partial update of a block blob's,
* use {@link BlockBlobClient.stageBlock} and {@link BlockBlobClient.commitBlockList}.
*
* This is a non-parallel uploading method, please use {@link BlockBlobClient.uploadFile},
* {@link BlockBlobClient.uploadStream} or {@link BlockBlobClient.uploadBrowserData} for better
* performance with concurrency uploading.
*
* @see https://learn.microsoft.com/rest/api/storageservices/put-blob
*
* @param blobName - Name of the block blob to create or update.
* @param body - Blob, string, ArrayBuffer, ArrayBufferView or a function
* which returns a new Readable stream whose offset is from data source beginning.
* @param contentLength - Length of body in bytes. Use Buffer.byteLength() to calculate body length for a
* string including non non-Base64/Hex-encoded characters.
* @param options - Options to configure the Block Blob Upload operation.
* @returns Block Blob upload response data and the corresponding BlockBlobClient instance.
*/
async uploadBlockBlob(blobName, body, contentLength, options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-uploadBlockBlob", options, async (updatedOptions) => {
const blockBlobClient = this.getBlockBlobClient(blobName);
const response = await blockBlobClient.upload(body, contentLength, updatedOptions);
return {
blockBlobClient,
response,
};
});
}
/**
* Marks the specified blob or snapshot for deletion. The blob is later deleted
* during garbage collection. Note that in order to delete a blob, you must delete
* all of its snapshots. You can delete both at the same time with the Delete
* Blob operation.
* @see https://learn.microsoft.com/rest/api/storageservices/delete-blob
*
* @param blobName -
* @param options - Options to Blob Delete operation.
* @returns Block blob deletion response data.
*/
async deleteBlob(blobName, options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-deleteBlob", options, async (updatedOptions) => {
let blobClient = this.getBlobClient(blobName);
if (options.versionId) {
blobClient = blobClient.withVersion(options.versionId);
}
return blobClient.delete(updatedOptions);
});
}
/**
* listBlobFlatSegment returns a single segment of blobs starting from the
* specified Marker. Use an empty Marker to start enumeration from the beginning.
* After getting a segment, process it, and then call listBlobsFlatSegment again
* (passing the the previously-returned Marker) to get the next segment.
* @see https://learn.microsoft.com/rest/api/storageservices/list-blobs
*
* @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
* @param options - Options to Container List Blob Flat Segment operation.
*/
async listBlobFlatSegment(marker, options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-listBlobFlatSegment", options, async (updatedOptions) => {
const response = (0, utils_common_js_1.assertResponse)(await this.containerContext.listBlobFlatSegment({
marker,
...options,
tracingOptions: updatedOptions.tracingOptions,
}));
const wrappedResponse = {
...response,
_response: {
...response._response,
parsedBody: (0, utils_common_js_1.ConvertInternalResponseOfListBlobFlat)(response._response.parsedBody),
}, // _response is made non-enumerable
segment: {
...response.segment,
blobItems: response.segment.blobItems.map((blobItemInternal) => {
const blobItem = {
...blobItemInternal,
name: (0, utils_common_js_1.BlobNameToString)(blobItemInternal.name),
tags: (0, utils_common_js_1.toTags)(blobItemInternal.blobTags),
objectReplicationSourceProperties: (0, utils_common_js_1.parseObjectReplicationRecord)(blobItemInternal.objectReplicationMetadata),
};
return blobItem;
}),
},
};
return wrappedResponse;
});
}
/**
* listBlobHierarchySegment returns a single segment of blobs starting from
* the specified Marker. Use an empty Marker to start enumeration from the
* beginning. After getting a segment, process it, and then call listBlobsHierarchicalSegment
* again (passing the the previously-returned Marker) to get the next segment.
* @see https://learn.microsoft.com/rest/api/storageservices/list-blobs
*
* @param delimiter - The character or string used to define the virtual hierarchy
* @param marker - A string value that identifies the portion of the list to be returned with the next list operation.
* @param options - Options to Container List Blob Hierarchy Segment operation.
*/
async listBlobHierarchySegment(delimiter, marker, options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-listBlobHierarchySegment", options, async (updatedOptions) => {
const response = (0, utils_common_js_1.assertResponse)(await this.containerContext.listBlobHierarchySegment(delimiter, {
marker,
...options,
tracingOptions: updatedOptions.tracingOptions,
}));
const wrappedResponse = {
...response,
_response: {
...response._response,
parsedBody: (0, utils_common_js_1.ConvertInternalResponseOfListBlobHierarchy)(response._response.parsedBody),
}, // _response is made non-enumerable
segment: {
...response.segment,
blobItems: response.segment.blobItems.map((blobItemInternal) => {
const blobItem = {
...blobItemInternal,
name: (0, utils_common_js_1.BlobNameToString)(blobItemInternal.name),
tags: (0, utils_common_js_1.toTags)(blobItemInternal.blobTags),
objectReplicationSourceProperties: (0, utils_common_js_1.parseObjectReplicationRecord)(blobItemInternal.objectReplicationMetadata),
};
return blobItem;
}),
blobPrefixes: response.segment.blobPrefixes?.map((blobPrefixInternal) => {
const blobPrefix = {
...blobPrefixInternal,
name: (0, utils_common_js_1.BlobNameToString)(blobPrefixInternal.name),
};
return blobPrefix;
}),
},
};
return wrappedResponse;
});
}
/**
* Returns an AsyncIterableIterator for ContainerListBlobFlatSegmentResponse
*
* @param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the ContinuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The ContinuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* @param options - Options to list blobs operation.
*/
async *listSegments(marker, options = {}) {
let listBlobsFlatSegmentResponse;
if (!!marker || marker === undefined) {
do {
listBlobsFlatSegmentResponse = await this.listBlobFlatSegment(marker, options);
marker = listBlobsFlatSegmentResponse.continuationToken;
yield await listBlobsFlatSegmentResponse;
} while (marker);
}
}
/**
* Returns an AsyncIterableIterator of {@link BlobItem} objects
*
* @param options - Options to list blobs operation.
*/
async *listItems(options = {}) {
let marker;
for await (const listBlobsFlatSegmentResponse of this.listSegments(marker, options)) {
yield* listBlobsFlatSegmentResponse.segment.blobItems;
}
}
/**
* Returns an async iterable iterator to list all the blobs
* under the specified account.
*
* .byPage() returns an async iterable iterator to list the blobs in pages.
*
* ```ts snippet:ReadmeSampleListBlobs_Multiple
* import { BlobServiceClient } from "@azure/storage-blob";
* import { DefaultAzureCredential } from "@azure/identity";
*
* const account = "<account>";
* const blobServiceClient = new BlobServiceClient(
* `https://${account}.blob.core.windows.net`,
* new DefaultAzureCredential(),
* );
*
* const containerName = "<container name>";
* const containerClient = blobServiceClient.getContainerClient(containerName);
*
* // Example using `for await` syntax
* let i = 1;
* const blobs = containerClient.listBlobsFlat();
* for await (const blob of blobs) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
*
* // Example using `iter.next()` syntax
* i = 1;
* const iter = containerClient.listBlobsFlat();
* let { value, done } = await iter.next();
* while (!done) {
* console.log(`Blob ${i++}: ${value.name}`);
* ({ value, done } = await iter.next());
* }
*
* // Example using `byPage()` syntax
* i = 1;
* for await (const page of containerClient.listBlobsFlat().byPage({ maxPageSize: 20 })) {
* for (const blob of page.segment.blobItems) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
* }
*
* // Example using paging with a marker
* i = 1;
* let iterator = containerClient.listBlobsFlat().byPage({ maxPageSize: 2 });
* let response = (await iterator.next()).value;
* // Prints 2 blob names
* if (response.segment.blobItems) {
* for (const blob of response.segment.blobItems) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
* }
* // Gets next marker
* let marker = response.continuationToken;
* // Passing next marker as continuationToken
* iterator = containerClient.listBlobsFlat().byPage({ continuationToken: marker, maxPageSize: 10 });
* response = (await iterator.next()).value;
* // Prints 10 blob names
* if (response.segment.blobItems) {
* for (const blob of response.segment.blobItems) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
* }
* ```
*
* @param options - Options to list blobs.
* @returns An asyncIterableIterator that supports paging.
*/
listBlobsFlat(options = {}) {
const include = [];
if (options.includeCopy) {
include.push("copy");
}
if (options.includeDeleted) {
include.push("deleted");
}
if (options.includeMetadata) {
include.push("metadata");
}
if (options.includeSnapshots) {
include.push("snapshots");
}
if (options.includeVersions) {
include.push("versions");
}
if (options.includeUncommitedBlobs) {
include.push("uncommittedblobs");
}
if (options.includeTags) {
include.push("tags");
}
if (options.includeDeletedWithVersions) {
include.push("deletedwithversions");
}
if (options.includeImmutabilityPolicy) {
include.push("immutabilitypolicy");
}
if (options.includeLegalHold) {
include.push("legalhold");
}
if (options.prefix === "") {
options.prefix = undefined;
}
const updatedOptions = {
...options,
...(include.length > 0 ? { include: include } : {}),
};
// AsyncIterableIterator to iterate over blobs
const iter = this.listItems(updatedOptions);
return {
/**
* The next method, part of the iteration protocol
*/
next() {
return iter.next();
},
/**
* The connection to the async iterator, part of the iteration protocol
*/
[Symbol.asyncIterator]() {
return this;
},
/**
* Return an AsyncIterableIterator that works a page at a time
*/
byPage: (settings = {}) => {
return this.listSegments(settings.continuationToken, {
maxPageSize: settings.maxPageSize,
...updatedOptions,
});
},
};
}
/**
* Returns an AsyncIterableIterator for ContainerListBlobHierarchySegmentResponse
*
* @param delimiter - The character or string used to define the virtual hierarchy
* @param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the ContinuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The ContinuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* @param options - Options to list blobs operation.
*/
async *listHierarchySegments(delimiter, marker, options = {}) {
let listBlobsHierarchySegmentResponse;
if (!!marker || marker === undefined) {
do {
listBlobsHierarchySegmentResponse = await this.listBlobHierarchySegment(delimiter, marker, options);
marker = listBlobsHierarchySegmentResponse.continuationToken;
yield await listBlobsHierarchySegmentResponse;
} while (marker);
}
}
/**
* Returns an AsyncIterableIterator for {@link BlobPrefix} and {@link BlobItem} objects.
*
* @param delimiter - The character or string used to define the virtual hierarchy
* @param options - Options to list blobs operation.
*/
async *listItemsByHierarchy(delimiter, options = {}) {
let marker;
for await (const listBlobsHierarchySegmentResponse of this.listHierarchySegments(delimiter, marker, options)) {
const segment = listBlobsHierarchySegmentResponse.segment;
if (segment.blobPrefixes) {
for (const prefix of segment.blobPrefixes) {
yield {
kind: "prefix",
...prefix,
};
}
}
for (const blob of segment.blobItems) {
yield { kind: "blob", ...blob };
}
}
}
/**
* Returns an async iterable iterator to list all the blobs by hierarchy.
* under the specified account.
*
* .byPage() returns an async iterable iterator to list the blobs by hierarchy in pages.
*
* ```ts snippet:ReadmeSampleListBlobsByHierarchy
* import { BlobServiceClient } from "@azure/storage-blob";
* import { DefaultAzureCredential } from "@azure/identity";
*
* const account = "<account>";
* const blobServiceClient = new BlobServiceClient(
* `https://${account}.blob.core.windows.net`,
* new DefaultAzureCredential(),
* );
*
* const containerName = "<container name>";
* const containerClient = blobServiceClient.getContainerClient(containerName);
*
* // Example using `for await` syntax
* let i = 1;
* const blobs = containerClient.listBlobsByHierarchy("/");
* for await (const blob of blobs) {
* if (blob.kind === "prefix") {
* console.log(`\tBlobPrefix: ${blob.name}`);
* } else {
* console.log(`\tBlobItem: name - ${blob.name}`);
* }
* }
*
* // Example using `iter.next()` syntax
* i = 1;
* const iter = containerClient.listBlobsByHierarchy("/");
* let { value, done } = await iter.next();
* while (!done) {
* if (value.kind === "prefix") {
* console.log(`\tBlobPrefix: ${value.name}`);
* } else {
* console.log(`\tBlobItem: name - ${value.name}`);
* }
* ({ value, done } = await iter.next());
* }
*
* // Example using `byPage()` syntax
* i = 1;
* for await (const page of containerClient.listBlobsByHierarchy("/").byPage({ maxPageSize: 20 })) {
* const segment = page.segment;
* if (segment.blobPrefixes) {
* for (const prefix of segment.blobPrefixes) {
* console.log(`\tBlobPrefix: ${prefix.name}`);
* }
* }
* for (const blob of page.segment.blobItems) {
* console.log(`\tBlobItem: name - ${blob.name}`);
* }
* }
*
* // Example using paging with a marker
* i = 1;
* let iterator = containerClient.listBlobsByHierarchy("/").byPage({ maxPageSize: 2 });
* let response = (await iterator.next()).value;
* // Prints 2 blob names
* if (response.blobPrefixes) {
* for (const prefix of response.blobPrefixes) {
* console.log(`\tBlobPrefix: ${prefix.name}`);
* }
* }
* if (response.segment.blobItems) {
* for (const blob of response.segment.blobItems) {
* console.log(`\tBlobItem: name - ${blob.name}`);
* }
* }
* // Gets next marker
* let marker = response.continuationToken;
* // Passing next marker as continuationToken
* iterator = containerClient
* .listBlobsByHierarchy("/")
* .byPage({ continuationToken: marker, maxPageSize: 10 });
* response = (await iterator.next()).value;
* // Prints 10 blob names
* if (response.blobPrefixes) {
* for (const prefix of response.blobPrefixes) {
* console.log(`\tBlobPrefix: ${prefix.name}`);
* }
* }
* if (response.segment.blobItems) {
* for (const blob of response.segment.blobItems) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
* }
* ```
*
* @param delimiter - The character or string used to define the virtual hierarchy
* @param options - Options to list blobs operation.
*/
listBlobsByHierarchy(delimiter, options = {}) {
if (delimiter === "") {
throw new RangeError("delimiter should contain one or more characters");
}
const include = [];
if (options.includeCopy) {
include.push("copy");
}
if (options.includeDeleted) {
include.push("deleted");
}
if (options.includeMetadata) {
include.push("metadata");
}
if (options.includeSnapshots) {
include.push("snapshots");
}
if (options.includeVersions) {
include.push("versions");
}
if (options.includeUncommitedBlobs) {
include.push("uncommittedblobs");
}
if (options.includeTags) {
include.push("tags");
}
if (options.includeDeletedWithVersions) {
include.push("deletedwithversions");
}
if (options.includeImmutabilityPolicy) {
include.push("immutabilitypolicy");
}
if (options.includeLegalHold) {
include.push("legalhold");
}
if (options.prefix === "") {
options.prefix = undefined;
}
const updatedOptions = {
...options,
...(include.length > 0 ? { include: include } : {}),
};
// AsyncIterableIterator to iterate over blob prefixes and blobs
const iter = this.listItemsByHierarchy(delimiter, updatedOptions);
return {
/**
* The next method, part of the iteration protocol
*/
async next() {
return iter.next();
},
/**
* The connection to the async iterator, part of the iteration protocol
*/
[Symbol.asyncIterator]() {
return this;
},
/**
* Return an AsyncIterableIterator that works a page at a time
*/
byPage: (settings = {}) => {
return this.listHierarchySegments(delimiter, settings.continuationToken, {
maxPageSize: settings.maxPageSize,
...updatedOptions,
});
},
};
}
/**
* The Filter Blobs operation enables callers to list blobs in the container whose tags
* match a given search expression.
*
* @param tagFilterSqlExpression - The where parameter enables the caller to query blobs whose tags match a given expression.
* The given expression must evaluate to true for a blob to be returned in the results.
* The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
* however, only a subset of the OData filter syntax is supported in the Blob service.
* @param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the continuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The continuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* @param options - Options to find blobs by tags.
*/
async findBlobsByTagsSegment(tagFilterSqlExpression, marker, options = {}) {
return tracing_js_1.tracingClient.withSpan("ContainerClient-findBlobsByTagsSegment", options, async (updatedOptions) => {
const response = (0, utils_common_js_1.assertResponse)(await this.containerContext.filterBlobs({
abortSignal: options.abortSignal,
where: tagFilterSqlExpression,
marker,
maxPageSize: options.maxPageSize,
tracingOptions: updatedOptions.tracingOptions,
}));
const wrappedResponse = {
...response,
_response: response._response, // _response is made non-enumerable
blobs: response.blobs.map((blob) => {
let tagValue = "";
if (blob.tags?.blobTagSet.length === 1) {
tagValue = blob.tags.blobTagSet[0].value;
}
return { ...blob, tags: (0, utils_common_js_1.toTags)(blob.tags), tagValue };
}),
};
return wrappedResponse;
});
}
/**
* Returns an AsyncIterableIterator for ContainerFindBlobsByTagsSegmentResponse.
*
* @param tagFilterSqlExpression - The where parameter enables the caller to query blobs whose tags match a given expression.
* The given expression must evaluate to true for a blob to be returned in the results.
* The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
* however, only a subset of the OData filter syntax is supported in the Blob service.
* @param marker - A string value that identifies the portion of
* the list of blobs to be returned with the next listing operation. The
* operation returns the continuationToken value within the response body if the
* listing operation did not return all blobs remaining to be listed
* with the current page. The continuationToken value can be used as the value for
* the marker parameter in a subsequent call to request the next page of list
* items. The marker value is opaque to the client.
* @param options - Options to find blobs by tags.
*/
async *findBlobsByTagsSegments(tagFilterSqlExpression, marker, options = {}) {
let response;
if (!!marker || marker === undefined) {
do {
response = await this.findBlobsByTagsSegment(tagFilterSqlExpression, marker, options);
response.blobs = response.blobs || [];
marker = response.continuationToken;
yield response;
} while (marker);
}
}
/**
* Returns an AsyncIterableIterator for blobs.
*
* @param tagFilterSqlExpression - The where parameter enables the caller to query blobs whose tags match a given expression.
* The given expression must evaluate to true for a blob to be returned in the results.
* The[OData - ABNF] filter syntax rule defines the formal grammar for the value of the where query parameter;
* however, only a subset of the OData filter syntax is supported in the Blob service.
* @param options - Options to findBlobsByTagsItems.
*/
async *findBlobsByTagsItems(tagFilterSqlExpression, options = {}) {
let marker;
for await (const segment of this.findBlobsByTagsSegments(tagFilterSqlExpression, marker, options)) {
yield* segment.blobs;
}
}
/**
* Returns an async iterable iterator to find all blobs with specified tag
* under the specified container.
*
* .byPage() returns an async iterable iterator to list the blobs in pages.
*
* Example using `for await` syntax:
*
* ```ts snippet:ReadmeSampleFindBlobsByTags
* import { BlobServiceClient } from "@azure/storage-blob";
* import { DefaultAzureCredential } from "@azure/identity";
*
* const account = "<account>";
* const blobServiceClient = new BlobServiceClient(
* `https://${account}.blob.core.windows.net`,
* new DefaultAzureCredential(),
* );
*
* const containerName = "<container name>";
* const containerClient = blobServiceClient.getContainerClient(containerName);
*
* // Example using `for await` syntax
* let i = 1;
* for await (const blob of containerClient.findBlobsByTags("tagkey='tagvalue'")) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
*
* // Example using `iter.next()` syntax
* i = 1;
* const iter = containerClient.findBlobsByTags("tagkey='tagvalue'");
* let { value, done } = await iter.next();
* while (!done) {
* console.log(`Blob ${i++}: ${value.name}`);
* ({ value, done } = await iter.next());
* }
*
* // Example using `byPage()` syntax
* i = 1;
* for await (const page of containerClient
* .findBlobsByTags("tagkey='tagvalue'")
* .byPage({ maxPageSize: 20 })) {
* for (const blob of page.blobs) {
* console.log(`Blob ${i++}: ${blob.name}`);
* }
* }
*
* // Example using paging with a marker
* i = 1;