Search This Blog

Saturday, June 6, 2020

Large file upload(>200MB) SharePoint online- using JSOM

<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script type="text/javascript">
    ExecuteOrDelayUntilScriptLoaded(function () {
        var scriptbase = _spPageContextInfo.siteAbsoluteUrl + "/_layouts/15/";

        // Load the js files and continue to the successHandler
        $.getScript(scriptbase + "SP.RequestExecutor.js"function () {
            console.log("request executor is now loaded");
            // Logic here
        });
    }, "sp.js");

    var siteUrl = _spPageContextInfo.webAbsoluteUrl;
    var documentLibrary = "Shared Documents";
    var fileName = "dcomts";
    var currentDlg = null;
    var siteRelativeUrl = _spPageContextInfo.webServerRelativeUrl != "/" ? _spPageContextInfo.webServerRelativeUrl : "";

    var fileUpload = function (file, documentLibrary, fileName) {
        return new Promise((resolve, reject) => {
            this.createDummyFile("dummy""Shared Documents").then(result => {
                var dlgTitle;
                var dlgMsg;
                var dlgWidth;
                var dlgHeight;
                dlgTitle = "Uploading Your File...";
                dlgMsg = "<br />Please wait whilst your file is being uploaded<br /><br />Please do not close your browser window.";
                dlgHeight = 200;
                dlgWidth = 500;

                if (currentDlg == null) {
                    currentDlg = SP.UI.ModalDialog.showWaitScreenWithNoClose(dlgTitle, dlgMsg, dlgHeight, dlgWidth);
                }
                var file = jQuery("#AttachmentUploadField")[0].files[0];
                let fr = new FileReader();
                let offset = 0;
                // the total file size in bytes...  
                let total = file.size;
                // 1MB Chunks as represented in bytes (if the file is less than a MB, seperate it into two chunks of 80% and 20% the size)...  
                let length = parseInt(1000000) > total ? Math.round(total * 0.8) : parseInt(1000000);
                let chunks = [];
                //reads in the file using the fileReader HTML5 API (as an ArrayBuffer) - readAsBinaryString is not available in IE!  
                fr.readAsArrayBuffer(file);
                fr.onload = (evt) => {
                    while (offset < total) {
                        //if we are dealing with the final chunk, we need to know...  
                        if (offset + length > total) {
                            length = total - offset;
                        }
                        //work out the chunks that need to be processed and the associated REST method (start, continue or finish)  
                        chunks.push({
                            offset,
                            length,
                            method: this.getUploadMethod(offset, length, total)
                        });
                        offset += length;
                    }
                    //each chunk is worth a percentage of the total size of the file...  
                    const chunkPercentage = (total / chunks.length) / total * 100;
                    if (chunks.length > 0) {
                        //the unique guid identifier to be used throughout the upload session  
                        const id = this.guid();
                        var file = jQuery("#AttachmentUploadField")[0].files[0];
                        //Start the upload - send the data to S  
                        this.uploadFile(evt.target.result, id, "Shared Documents", file.name, chunks, 00, chunkPercentage, resolve, reject);
                    }
                };
            })
        });
    }

    function createDummyFile(fileName, libraryName) {
        return new Promise((resolve, reject) => {
            // Construct the endpoint - The GetList method is available for SharePoint Online only.  
            var serverRelativeUrlToFolder = "decodedurl='" + this.siteRelativeUrl + "/" + libraryName + "'";
            var file = jQuery("#AttachmentUploadField")[0].files[0];
            currentDlg = null;
            var endpoint = this.siteUrl + "/_api/Web/GetFolderByServerRelativePath(" + serverRelativeUrlToFolder + ")/files" + "/add(overwrite=true, url='" + file.name + "')"
            const headers = {
                "accept""application/json;odata=verbose"
            };
            this.executeAsync(endpoint, this.convertDataBinaryString(2), headers).then(file => resolve(true)).catch(err => reject(err));
        });
    }
    // Base64 - this method converts the blob arrayBuffer into a binary string to send in the REST request  
    function convertDataBinaryString(data) {
        let fileData = '';
        let byteArray = new Uint8Array(data);
        for (var i = 0; i < byteArray.byteLength; i++) {
            fileData += String.fromCharCode(byteArray[i]);
        }
        return fileData;
    }
    function executeAsync(endPointUrl, data, requestHeaders) {
        return new Promise((resolve, reject) => {
            // using a utils function we would get the APP WEB url value and pass it into the constructor...  
            let executor = new SP.RequestExecutor(this.siteUrl);
            // Send the request.  
            executor.executeAsync({
                url: endPointUrl,
                method: "POST",
                body: data,
                binaryStringRequestBody: true,
                headers: requestHeaders,
                success: offset => resolve(offset),
                error: err => reject(err.responseText)
            });
        });
    }
    //this method sets up the REST request and then sends the chunk of file along with the unique indentifier (uploadId)  
    function uploadFileChunk(id, libraryPath, fileName, chunk, data, byteOffset) {
        return new Promise((resolve, reject) => {
            let offset = chunk.offset === 0 ? '' : ',fileOffset=' + chunk.offset;
            //parameterising the components of this endpoint avoids the max url length problem in SP (Querystring parameters are not included in this length)  
            let endpoint = this.siteUrl + "/_api/web/getfilebyserverrelativeurl('" + this.siteRelativeUrl + "/" + libraryPath + "/" + fileName + "')/" + chunk.method + "(uploadId=guid'" + id + "'" + offset + ")";
            const headers = {
                "Accept""application/json; odata=verbose",
                "Content-Type""application/octet-stream"
            };
            this.executeAsync(endpoint, data, headers).then(offset => resolve(offset)).catch(err => reject(err));
        });
    }
    //the primary method that resursively calls to get the chunks and upload them to the library (to make the complete file)  
    function uploadFile(result, id, libraryPath, fileName, chunks, index, byteOffset, chunkPercentage, resolve, reject) {
        //we slice the file blob into the chunk we need to send in this request (byteOffset tells us the start position)  
        const data = this.convertFileToBlobChunks(result, byteOffset, chunks[index]);
        //upload the chunk to the server using REST, using the unique upload guid as the identifier  
        this.uploadFileChunk(id, libraryPath, fileName, chunks[index], data, byteOffset).then(value => {
            const isFinished = index === chunks.length - 1;
            index += 1;
            const percentageComplete = isFinished ? 100 : Math.round((index * chunkPercentage));
            //More chunks to process before the file is finished, continue  
            if (index < chunks.length) {
                this.uploadFile(result, id, libraryPath, fileName, chunks, index, byteOffset, chunkPercentage, resolve, reject);
            } else {
                resolve(value);
                if (currentDlg != null) {
                    currentDlg.close();
                }
            }
        }).catch(err => {
            console.log('Error in uploadFileChunk! ' + err);
            reject(err);
        });
    }
    //Helper method - depending on what chunk of data we are dealing with, we need to use the correct REST method...  
    function getUploadMethod(offset, length, total) {
        if (offset + length + 1 > total) {
            return 'finishupload';
        } else if (offset === 0) {
            return 'startupload';
        } else if (offset < total) {
            return 'continueupload';
        }
        return null;
    }
    //this method slices the blob array buffer to the appropriate chunk and then calls off to get the BinaryString of that chunk  
    function convertFileToBlobChunks(result, byteOffset, chunkInfo) {
        let arrayBuffer = result.slice(chunkInfo.offset, chunkInfo.offset + chunkInfo.length);
        return this.convertDataBinaryString(arrayBuffer);
    }
    function guid() {
        function s4() {
            return Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1);
        }
        return s4() + s4() + '-' + s4() + '-' + s4() + '-' + s4() + '-' + s4() + s4() + s4();
    }

</script>

<div class="div">
    <input id="AttachmentUploadField" type="file" size="98" />
    <input id="AttachAttachmentButton" class="button" onclick="fileUpload()" type="button" value="Upload" />
</div>
<div class="div">
    <label id="lblResult" class="result"></label>
</div>