in src/blob/handlers/PageBlobHandler.ts [167:264]
public async uploadPages(
body: NodeJS.ReadableStream,
contentLength: number,
options: Models.PageBlobUploadPagesOptionalParams,
context: Context
): Promise<Models.PageBlobUploadPagesResponse> {
const blobCtx = new BlobStorageContext(context);
const accountName = blobCtx.account!;
const containerName = blobCtx.container!;
const blobName = blobCtx.blob!;
const date = blobCtx.startTime!;
if (contentLength % 512 !== 0) {
throw StorageErrorFactory.getInvalidOperation(
blobCtx.contextId!,
"content-length or x-ms-content-length must be aligned to a 512-byte boundary."
);
}
const blob = await this.metadataStore.downloadBlob(
context,
accountName,
containerName,
blobName,
undefined,
options.leaseAccessConditions
);
if (blob.properties.blobType !== Models.BlobType.PageBlob) {
throw StorageErrorFactory.getBlobInvalidBlobType(blobCtx.contextId!);
}
// Check Lease status
new BlobWriteLeaseValidator(options.leaseAccessConditions).validate(
new BlobLeaseAdapter(blob),
context
);
let ranges;
try {
ranges = deserializePageBlobRangeHeader(
blobCtx.request!.getHeader("range"),
blobCtx.request!.getHeader("x-ms-range"),
true
);
} catch (err) {
throw StorageErrorFactory.getInvalidPageRange(blobCtx.contextId!);
}
const start = ranges[0];
const end = ranges[1]; // Inclusive
if (end - start + 1 !== contentLength) {
throw StorageErrorFactory.getInvalidPageRange(blobCtx.contextId!);
}
// Start Range is bigger than blob length
if (start >= blob.properties.contentLength!) {
throw StorageErrorFactory.getInvalidPageRange(blobCtx.contextId!);
}
const persistency = await this.extentStore.appendExtent(
body,
context.contextId
);
if (persistency.count !== contentLength) {
// TODO: Confirm status code
throw StorageErrorFactory.getInvalidOperation(
blobCtx.contextId!,
`The size of the request body ${persistency.count} mismatches the content-length ${contentLength}.`
);
}
const res = await this.metadataStore.uploadPages(
context,
blob,
start,
end,
persistency,
options.leaseAccessConditions,
options.modifiedAccessConditions,
options.sequenceNumberAccessConditions
);
const response: Models.PageBlobUploadPagesResponse = {
statusCode: 201,
eTag: res.etag,
lastModified: date,
contentMD5: undefined, // TODO
blobSequenceNumber: res.blobSequenceNumber,
requestId: blobCtx.contextId,
version: BLOB_API_VERSION,
date,
isServerEncrypted: true,
clientRequestId: options.requestId
};
return response;
}