Skip to content

Instantly share code, notes, and snippets.

@rndazurescript
Created July 14, 2020 12:13
Show Gist options
  • Save rndazurescript/fda86d7ad140a7c1de5e3274c80a1397 to your computer and use it in GitHub Desktop.
Save rndazurescript/fda86d7ad140a7c1de5e3274c80a1397 to your computer and use it in GitHub Desktop.
Old blob uploader in typescript
module UpZure.Blob {
// An uploader client
// based on https://msdn.microsoft.com/en-us/library/azure/mt427365.aspx
// Limitations taken from https://msdn.microsoft.com/en-us/library/azure/dd135726.aspx
export class BlobUploader {
private MAX_BLOB_SIZE = 4 * 1024 * 1024;//Each file will be split in 4Mb (used to be 256 KB).
private BLOCK_NAME_PREFIX = "blk-";
private MAX_BLOCKS = 50000;//a maximum of 50,000 blocks
private fileReader: FileReader;
private handleError(desc: string, err: string) {
console.error(desc, err);
}
private handlePercentCompleted(value: string) {
console.info("Completed " + value + "%");
}
private handleFinish() {
console.info("Finished uploading");
}
constructor(private url: string, private file: File) {
// Check if all the File APIs are supported.
if (!(File && FileReader && FileList && Blob)) {
throw new Error('The File APIs are not fully supported in this browser.');
}
this.totalSize = file.size;
this.fileReader = new FileReader();
this.fileReader.onloadend = (evt: IFileReaderProgressEvent) => {
if (evt.target.readyState == FileReaderReadyState.DONE) {
var uri = url + '&comp=block&blockid=' + this.blockIds[this.blockIds.length - 1];
var requestData = new Uint8Array(evt.target.result);
$.ajax({
url: uri,
type: "PUT",
data: requestData,
processData: false,
beforeSend: function (xhr) {
xhr.setRequestHeader('x-ms-blob-type', 'BlockBlob');
},
success: (data, status) => {
// update current indexes
this.currentFilePointer += requestData.length;
this.remainingBytes -= requestData.length;
// Calculate percent
var percentComplete = ((this.currentFilePointer / this.totalSize) * 100).toFixed(2);
this.handlePercentCompleted(percentComplete);
// And move along
this.processRemainingFile();
},
error: (xhr, desc, err) => {
this.handleError(desc, err);
}
});
}
};
}
// The size of the file to upload
private totalSize: number = 0;
// The bytes left to upload
private remainingBytes: number = 0;
// Current point in the file
private currentFilePointer: number = 0;
// The ids of the uploaded blocks
private blockIds: string[] = [];
// Flag to stop processing
private stopProcessing: boolean = false;
private getNextBlockId(): string {
// We need a string
var currentId = this.blockIds.length + '';
// We pad it with 0's (50k is the max number so)
currentId = "00000".substring(currentId.length) + currentId;
return this.BLOCK_NAME_PREFIX + currentId;
}
private processRemainingFile() {
if (this.stopProcessing) return;
if (this.remainingBytes > 0) {
var bytesToRead = this.MAX_BLOB_SIZE;
// If close the end
if (this.remainingBytes < bytesToRead) {
bytesToRead = this.remainingBytes;
}
// Get a slice of the file
var fileContent = this.file.slice(this.currentFilePointer, this.currentFilePointer + bytesToRead);
var blockName = this.getNextBlockId();
this.blockIds.push(btoa(blockName));
// and read it
this.fileReader.readAsArrayBuffer(fileContent);
} else {
this.commitBlockList();
}
}
private commitBlockList() {
var uri = this.url + '&comp=blocklist';
var requestBody = '<?xml version="1.0" encoding="utf-8"?><BlockList>';
for (var i = 0; i < this.blockIds.length; i++) {
requestBody += '<Latest>' + this.blockIds[i] + '</Latest>';
}
requestBody += '</BlockList>';
$.ajax({
url: uri,
type: "PUT",
data: requestBody,
beforeSend: (xhr) => {
xhr.setRequestHeader('x-ms-blob-content-type', this.file.type);
xhr.setRequestHeader('Content-Length', ''+ requestBody.length);
},
success: (data, status) => {
this.handleFinish();
},
error: (xhr, desc, err) => {
this.handleError(desc, err);
}
});
}
public Upload(finishCb: ISuccessCallback = null, errorCb: IErrorCallback = null, notifyPercent: INotifyPercent = null) {
if (finishCb != null) {
this.handleFinish = finishCb;
}
if (errorCb != null) {
this.handleError = errorCb;
}
if (notifyPercent != null) {
this.handlePercentCompleted = notifyPercent;
}
this.remainingBytes = this.totalSize;
this.currentFilePointer = 0;
this.blockIds = [];
this.stopProcessing = false;
this.processRemainingFile();
}
public CancelUpload() {
this.stopProcessing = true;
}
}
interface IFileReaderProgressEvent extends ProgressEvent {
target: FileReaderEventTarget
}
/* File API Extensions*/
interface FileReaderEventTarget extends EventTarget {
//https://developer.mozilla.org/en-US/docs/Web/API/FileReader/readyState
// EMPTY : 0 : No data has been loaded yet.
// LOADING: 1 : Data is currently being loaded.
// DONE : 2 : The entire read request has been completed.
readyState: number;
result: ArrayBuffer;
}
enum FileReaderReadyState {
EMPTY = 0,
LOADING = 1,
DONE = 2
}
export interface INotifyPercent {
(value: string): void;
}
export interface ISuccessCallback {
(): void;
}
export interface IErrorCallback {
(desc: string, err: string): void;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment