in src/blob/handlers/AppendBlobHandler.ts [101:218]
public async appendBlock(
body: NodeJS.ReadableStream,
contentLength: number,
options: Models.AppendBlobAppendBlockOptionalParams,
context: Context
): Promise<Models.AppendBlobAppendBlockResponse> {
const blobCtx = new BlobStorageContext(context);
const accountName = blobCtx.account!;
const containerName = blobCtx.container!;
const blobName = blobCtx.blob!;
const date = blobCtx.startTime!;
if (contentLength > MAX_APPEND_BLOB_BLOCK_SIZE) {
throw StorageErrorFactory.getRequestEntityTooLarge(blobCtx.contextId);
}
if (contentLength === 0) {
throw StorageErrorFactory.getInvalidHeaderValue(blobCtx.contextId, {
HeaderName: HeaderConstants.CONTENT_LENGTH,
HeaderValue: "0"
});
}
// TODO: Optimize with cache
const blob = await this.metadataStore.downloadBlob(
blobCtx,
accountName,
containerName,
blobName,
undefined
);
if (blob.properties.blobType !== Models.BlobType.AppendBlob) {
throw StorageErrorFactory.getBlobInvalidBlobType(blobCtx.contextId);
}
const committedBlockCount = (blob.committedBlocksInOrder || []).length;
if (committedBlockCount >= MAX_APPEND_BLOB_BLOCK_COUNT) {
throw StorageErrorFactory.getBlockCountExceedsLimit(blobCtx.contextId);
}
// Persist content
const extent = await this.extentStore.appendExtent(body, blobCtx.contextId);
if (extent.count !== contentLength) {
throw StorageErrorFactory.getInvalidOperation(
blobCtx.contextId,
`The size of the request body ${extent.count} mismatches the content-length ${contentLength}.`
);
}
// MD5
const contentMD5 = blobCtx.request!.getHeader(HeaderConstants.CONTENT_MD5);
let contentMD5Buffer;
let contentMD5String;
if (contentMD5 !== undefined) {
contentMD5Buffer =
typeof contentMD5 === "string"
? Buffer.from(contentMD5, "base64")
: contentMD5;
contentMD5String =
typeof contentMD5 === "string"
? contentMD5
: contentMD5Buffer.toString("base64");
const stream = await this.extentStore.readExtent(
extent,
blobCtx.contextId
);
const calculatedContentMD5Buffer = await getMD5FromStream(stream);
const calculatedContentMD5String = Buffer.from(
calculatedContentMD5Buffer
).toString("base64");
if (contentMD5String !== calculatedContentMD5String) {
throw StorageErrorFactory.getMd5Mismatch(
context.contextId,
contentMD5String,
calculatedContentMD5String
);
}
}
const originOffset = blob.properties.contentLength;
const properties = await this.metadataStore.appendBlock(
blobCtx,
{
accountName,
containerName,
blobName,
isCommitted: true,
name: "", // No block ID for append block
size: extent.count,
persistency: extent
},
options.leaseAccessConditions,
options.modifiedAccessConditions,
options.appendPositionAccessConditions
);
const response: Models.AppendBlobAppendBlockResponse = {
statusCode: 201,
requestId: context.contextId,
eTag: properties.etag,
lastModified: properties.lastModified,
contentMD5: contentMD5Buffer,
xMsContentCrc64: undefined,
clientRequestId: options.requestId,
version: BLOB_API_VERSION,
date,
blobAppendOffset: `${originOffset}`,
blobCommittedBlockCount: committedBlockCount + 1,
isServerEncrypted: true
};
return response;
}