Ajax File & Folder Upload

The functionality described in this article is available in IT Hit WebDAV Ajax Library v5 Beta and later versions.

IT Hit WebDAV Ajax Library provides powerful upload functionality with pause and resume functionality. The library supports folders drag-and-drop and files multi-select functionality. You can upload files and folders in Chrome, FireFox, Edge, IE and Safari on Windows, Max OS X, Linux, iOS and Android.

During upload a developer is provided with almost any possible upload info, such as upload progress, remaining bytes, file size, remaining and elapsed time, error description, etc. You can fully program the look and fill of every detail of your upload UI, the library provides only core Ajax upload functionality and a sample code, leaving the UI implementation to the developer.

Getting Started With Upload

Here are the minimum steps required to create files and folders upload functionality. On your web page you will create Uploader class instance, set settings, assign drop zones (and inputs, if required), set event handlers to be triggered when file or folder is added to the upload queue. Than you will assign progress and item state change event handlers and render user interface.

  1. Create Uploader class instance and set upload URL using Uploader.SetUploadUrl() function. Optionally you can also set upload settings, such as FilesMultiselectUploadFolders, etc:

    var uploader = new ITHit.WebDAV.Client.Upload.Uploader();
    uploader.SetUploadUrl('https://webdavserver/path/');
    uploader.UploadFolders = true;
    uploader.FilesMultiselect = true;
  2. Set one or more drag-and-drop zones and file inputs. They will be used to initiate upload:

    uploader.DropZones.AddById('my-dropzone'); // ID of the HTML element used for drag-and-drop.
    uploader.Inputs.AddById('my-fileinput');   // File input ID.
  3. Assign Queue.OnQueueChanged even handler. This event will be fired when items are added or deleted from upload queue:

    uploader.Queue.AddListener('OnQueueChanged', '_CollectionChange', this);
    
    function _CollectionChange(oQueueChanged) {
        $.each(oQueueChanged.AddedItems, function (index, uploadItem) {
            CreateRow(uploadItem);
            ...
        }.bind(this));
    
        $.each(oQueueChanged.RemovedItems, function (index, uploadItem) {
            ...
        }.bind(this));
    };
  4. Assign UploadItem.OnProgressChanged and UploadItem.OnStateChanged event handlers to each item in the queue. You will typically do this in Uploader.OnQueueChanged event, when a new item is added to the queue: 

    function CreateRow(oUploadItem) {
        oUploadItem.AddListener('OnProgressChanged', '_OnProgress', this);
        oUploadItem.AddListener('OnStateChanged', '_OnStateChange', this);
        _Render(oUploadItem);
    };
    
    function _OnProgress(oProgressEvent) {
        _Render(oProgressEvent.Sender);
    };
    
    function _OnStateChange(oStateChangedEvent) {
        _Render(oStateChangedEvent.Sender);
    };
  5. Inside these event handlers you will get all required information about each item being uploaded and can update user interface. The UploadItem.GetProgress() function provides information about upload progress, the UploadItem.GetState() function provides info about item state:

    function _Render(oUploadItem) {
        var oProgress = oUploadItem.GetProgress();
        var columns = [
            oUploadItem.GetName(),
            oUploadItem.GetUrl(),
            oUploadItem.GetSize(),
            oProgress.UploadedBytes,
            oProgress.Completed,
            oProgress.ElapsedTime,
            oProgress.RemainingTime,
            oProgress.Speed,
            oUploadItem.GetState()
        ];
    ...
    }

Complete Ajax Upload Page Example

Below is a simple example of files and folders upload page implementation:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>IT Hit WebDAV Uploader</title>
    <script src="ITHitWebDAVClient.js" type="text/javascript"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
</head>
<body id="ithit-dropzone">
    <input id="ithit-input" class="d-none" type="file" multiple>
    <table class="table table-responsive ithit-grid-uploads">
        <thead>
            <tr>
                <th>Display Name</th>
                <th>Download Url</th>
                <th>Size</th>
                <th>Uploaded Bytes</th>
                <th>Completed</th>
                <th>Elapsed TimeSpan</th>
                <th>Remaining TimeSpan</th>
                <th>Speed</th>
                <th>State</th>
                <th>Actions</th>
            </tr>
        </thead>
        <tbody></tbody>
    </table>
    <script type="text/javascript">
        function UploaderGridView(sSelector) {
            this.Uploader = new ITHit.WebDAV.Client.Upload.Uploader();
            this.Uploader.DropZones.AddById('ithit-dropzone');
            this.Uploader.Inputs.AddById('ithit-input');
            this.Uploader.Queue.AddListener('OnQueueChanged', '_CollectionChange', this);
            this.$table = $(sSelector);
            this.rows = [];
        };

        /**
        * Observes adding and deleting of UploadItem and creates and removes rows in table.
        * @param {ITHit.WebDAV.Client.Upload.Queue#OnQueueChanged} oQueueChanged
        */
        UploaderGridView.prototype._CollectionChange = function (oQueueChanged) {
            $.each(oQueueChanged.AddedItems, function (index, value) {
                var row = new UploaderGridRow(value);
                this.rows.push(row);
                this.$table.append(row.$el);
            }.bind(this));

            $.each(oQueueChanged.RemovedItems, function (index, value) {
                var aRows = $.grep(this.rows, function (oElem) { return value === oElem.UploadItem; });
                var iIndex = this.rows.indexOf(aRows[0]);
                this.rows.splice(iIndex, 1);
                aRows[0].$el.remove();
            }.bind(this));
        };

        /**
        * Represents table row and subscribes for upload changes.
        * @param {ITHit.WebDAV.Client.Upload.UploadItem} oUploadItem
        */
        function UploaderGridRow(oUploadItem) {
            this.$el = $('<tr></tr>');
            this.oUploadItem = oUploadItem;
            this.oUploadItem.AddListener('OnProgressChanged', '_OnProgress', this);
            this.oUploadItem.AddListener('OnStateChanged', '_OnStateChange', this);
            this._Render(oUploadItem);
        };


        /**
        * Creates upload details view.
        * @param {ITHit.WebDAV.Client.Upload.UploadItem} oUploadItem
        */
        UploaderGridRow.prototype._Render = function (oUploadItem) {
            /* @typedef {ITHit.WebDAV.Client.Upload.Progress} oProgress */
            var oProgress = oUploadItem.GetProgress();
            var columns = [
                oUploadItem.GetName(),
                oUploadItem.GetUrl(),
                oUploadItem.GetSize(),
                oProgress.UploadedBytes,
                oProgress.Completed,
                oProgress.ElapsedTime,
                oProgress.RemainingTime,
                oProgress.Speed,
                oUploadItem.GetState()
            ];

            var $columns = [];
            columns.forEach(function (item) {
                var $column = $('<td></td>');
                $column.html(item);
                $columns.push($column);
            });

            var $actions = $('<td></td>');
            this._RenderActions(oUploadItem).forEach(function (item) {
                $actions.append(item);
            });

            $columns.push($actions);
            this.$el.empty();
            $columns.forEach(function (column) {
                this.$el.append(column);
            }, this);
        };

        /**
        * Creates upload actions view.
        * @param {ITHit.WebDAV.Client.Upload.UploadItem} oUploadItem
        */
        UploaderGridRow.prototype._RenderActions = function (oUploadItem) {
            var actions = [];
            actions.push($('<a></a>').
                html('<span class="glyphicon glyphicon-play"></span>').
                attr('href', 'javascript:void(0)').
                on('click',
                    function () {
                        oUploadItem.StartAsync();
                    }));

            actions.push($('<a></a>').
                html('<span class="glyphicon glyphicon-stop"></span>').
                attr('href', 'javascript:void(0)').
                on('click',
                    function () {
                        oUploadItem.CancelAsync();
                    }));

            return actions;
        };

        /**
        * Handles UploadItem state change.
        * @param {ITHit.WebDAV.Client.Upload.UploadItem#OnStateChanged} oStateChangedEvent
        */
        UploaderGridRow.prototype._OnStateChange = function (oStateChangedEvent) {
            this._Render(oStateChangedEvent.Sender);
        };

        /**
        * Handles UploadItem progress change.
        * @param {ITHit.WebDAV.Client.Upload.UploadItem#OnProgressChanged} oProgressEvent
        */
        UploaderGridRow.prototype._OnProgress = function (oProgressEvent) {
            this._Render(oProgressEvent.Sender);
        };

        var sUploadUrl = 'https://webdavserver/path/';
        var oUploaderGrid = new UploaderGridView('.ithit-grid-uploads');
        oUploaderGrid.Uploader.SetUploadUrl(sUploadUrl);
    </script>
</body>
</html>

Uploader Class

All classes required to implement upload are located in ITHit.WebDAV.Client.Upload namespace with the Uploader class being the starting point of your upload functionality implementation. It provides default upload settings and functions to add drop zones, file inputs and a list of files/folders being uploaded.

To set the URL to which files will be uploaded call Uploader.SetUploadUrl() function. In case of folders upload, new folders will be created under this URL.

Upload Queue and UploadItem Class

The Uploader.Queue property contains list of all files being uploaded. In case of folders upload the upload queue also contains a list of all folders that should be created. Each item in the list is an instance of UploadItem class and represents a file or folder being uploaded. Using UploadItem you can discover item state, get info about upload progress as well as pause, resume and cancel each item upload. 

To get file or folder upload state call UploadItem.GetState() function. It will return State enumeration indicating if the item is being uploaded, paused, canceled, an error occurred or is in any other state.

To get item progress call UploadItem.GetProgress() function. It returns a Progress object that contains upload information: number of bytes uploaded, file size, upload percent completed, upload speed, elapsed and remaining time. 

Pausing Upload

To pause upload call UploadItem.PauseAsync() function. This call will break the connection with the server for this file. After that PauseAsync() function requests how many bytes was saved on the server side. Note that typically bytes are being transmitted and saved to your storage in blocks. When connection breaks the last one block will not be saved and as a result, you typically will see that the progress has rolled back slightly. Finally PauseAsync() will call the callback function passed as a parameter.

Resuming Upload

To resume upload call UploadItem.StartAsync() function. This call will request mow many bytes were successfully saved on the server and then restart upload from the next byte.

Canceling Upload

To stop item upload call UploadItem.CancelAsync() function. This will break the connection with the server. Then, if the file was marked for deletion with UploadItem.SetDeleteOnCancel() call (it did not exist on the server before upload started), it will delete the file sending a DELETE request. In many cases breaking connection with the server will not release the file on the server immediately, the server will continue holding the file and the delete operation will fail. That is why the CancelAsync() function will retry deleting a file several times. The maximum number of retries can be passed as a CancelAsync() function parameter.

Finally the CancelAsync() function will send CANCELUPLOAD request. In case the file existed on the server before upload started, this call will signal to the server that the client will not retry upload of this file and any content upload could be deleted. The server can also restore the old file content.

Probing Resumable Upload Support

The ajax upload functionality is independent of server-side and most of its functionality (except pause/resume) can be used with any server that supports WebDAV standard. However, the pause/resume requires resumable upload support provided with IT Hit WebDAV Server Engine for .NET and IT Hit WebDAV Server Library for Java.

The WebDAV server that supports resumable upload returns resumable-upload token in DAV header in OPTIONS request:

DAV: 1,2,3,resumable-upload

The WebDAV Ajax Library provides HierarchyItem.GetSupportedFeaturesAsync() method that returns information about features supported by server, that are returned in the OptionsInfo.Features flags enumeration. The GetSupportedFeaturesAsync() call submits OPTIONS request to server and if search is supported the Features.ResumableUpload flag is set.

var webDavSession = new ITHit.WebDAV.Client.WebDavSession();

webDavSession.OpenFolderAsync('https://serv/', null, function(oAsyncResult){
    /** @typedef {ITHit.WebDAV.Client.Folder} oFolder */
    var oFolder = oAsyncResult.Result;

    oFolder.GetSupportedFeaturesAsync(function(oAsyncResult){
        /** @typedef {ITHit.WebDAV.Client.OptionsInfo} oOptionsInfo */
        var oOptionsInfo = oAsyncResult.Result;

        var bResumableUpload = oOptionsInfo.Features &
                ITHit.WebDAV.Client.Features.ResumableUpload !== 0;
        console.log('Resumable upload support: ' + (bResumableUpload ? 'yes' : 'no'));
    });
});

While the IT Hit WebDAV Server Engine for .NET v6.0 and earlier and IT Hit WebDAV Server Library for Java v3.1 and earlier support resumable upload functionality, they do not report resumable upload support when OPTIONS request is sent on a folder. The resumable upload support on folders is reported in IT Hit WebDAV Server Engine for .NET v6.1 and later or IT Hit WebDAV Server Library for Java v3.2 and later if IResumableUploadBase (in case of .NET) or ResumableUploadBase (in case of Java) are implemented on folder items.

Upload Validation

The functionality described in this section is available in IT Hit WebDAV Ajax Library v5.10 and later versions.

In many cases, you will need to validate selected items before the upload starts. You can verify file extensions, names, upload path, file size, a number of files selected as well as perform any other types of checks. You can also make requests to the server and verify file existence or do any other types of verifications.

In many cases, you will also check for the presence of not supported characters in file and folder names. Note that such validation is specific to your server implementation. The WebDAV standard itself as well as IT Hit WebDAV Server Engine(s) support any characters in files and folder names and does not have any limitations. 

To provide maximum performance and to avoid any extra requests to the server all files and folders presence validation checks are made in user code. The IT Hit WebDAV Ajax Library itself does not make any file and folder validation requests.

You will validate upload inside Queue.OnUploadItemsCreated and in UploadItem.OnBeforeUploadStarted that we will describe below.

Queue.OnUploadItemsCreated Event

Queue.OnUploadItemsCreated is fired when files and folders are selected for upload, before items are added to the upload queue. Here you have access to all items that user dropped inside your drag-and-drop area or select via input field, the list of items is passed as a function parameter. You can filter files and folders selected by the user, display UI, make requests to the server for item existence validation, etc. Here you will specify if the item should be overwritten as well as if the item should be automatically deleted if upload canceled.

The event parameter object, UploadItemsCreated, contains a list of all items selected for upload in the UploadItemsCreated.Items property. As soon as you may perform asynchronous calls in OnUploadItemsCreated event, to signal that all asynchronous calls are completed, you must call UploadItemsCreated.Upload() function passing the list of items to be uploaded. The Upload() function call will start the upload. Only items passed into Upload() function will be added to the upload queue. 

Inside OnUploadItemsCreated call, you can specify if a file should be overwritten, deleted when the upload is canceled or added to the queue in the failed state:

  • To force overwrite the existing file call UploadItem.SetOverwrite() passing true as a parameter. 
  • To delete a file when upload is canceled call UploadItem.SetDeleteOnCancel() passing true as a parameter.
  • To add a file to the queue in the failed state call UploadItem.SetFailed() passing a WebDavException instance. Such file will not start uploading until started with UploadItem.StartAsync() function.

Below you you can see a complete example of the event handler:

uploader.Queue.AddListener('OnUploadItemsCreated', this._OnUploadItemsCreated, this);

/** Called when a user selects items for upload or drops items into a drop area. 
*
* @param {ITHit.WebDAV.Client.Upload.Events.UploadItemsCreated} oUploadItemsCreated - Contains
* a list of items selected by the user for upload in UploadItemsCreated.Items property.
*/
UploaderGridView.prototype._OnUploadItemsCreated = function (oUploadItemsCreated) {

    /* Validate file extensions, size, name, etc. here. */

    /* Below we will check if each file exists on the server 
    and ask a user if files should be overwritten or skipped. */
    this._GetExistsAsync(oUploadItemsCreated.Items, function (oAsyncResult) {
        if (oAsyncResult.IsSuccess && oAsyncResult.Result.length === 0) {
            // No items exists on the server.                
            // Add all items to the upload queue.
            oUploadItemsCreated.Upload(oUploadItemsCreated.Items);
            return;
        }

        if (!oAsyncResult.IsSuccess) {
            // Some error occurred during item existence verification requests.
            // Show error dialog with error description.
            // Mark all items as failed and add to the upload list.
            this._ShowExistsCheckError(oAsyncResult.Error,
                function () {
                    oUploadItemsCreated.Items.forEach(function (oUploadItem) {

                        // Move an item into the error state. 
                        // Upload of this item will NOT start when added to the queue.
                        oUploadItem.SetFailed(oAsyncResult.Error);
                    });

                    // Add all items to the upload queue, so a user can start the upload later.
                    oUploadItemsCreated.Upload(oUploadItemsCreated.Items);
                });
            return;
        }

        var sItemsList = ''; // List of items to be displayed in Overwrite / Skip / Cancel dialog.

        /** @type {ITHit.WebDAV.Client.Upload.UploadItem[]} aExistsUploadItems */
        var aExistsUploadItems = [];
        oAsyncResult.Result.forEach(function (oUploadItem) {

            // For the sake of simplicity folders are never deleted when upload canceled.
            if (!oUploadItem.IsFolder()) {

                // File exists so we should not delete it when file's upload canceled.
                oUploadItem.SetDeleteOnCancel(false);
            }

            // Mark item as verified to avoid additional file existence verification requests.
            oUploadItem.CustomData.FileExistanceVerified = true;

            sItemsList += oUploadItem.GetRelativePath() + '<br/>';
            aExistsUploadItems.push(oUploadItem);
        });

        /* One or more items exists on the server. Show Overwrite / Skip / Cancel dialog.*/
        oConfirmModal.Confirm(pasteFormat(sOverwriteDialogueFormat, sItemsList),

            /* A user selected to overwrite existing files. */
            function onOverwrite() {

                // Mark all items that exist on the server with overwrite flag.
                aExistsUploadItems.forEach(function (oUploadItem) {
                    if (oUploadItem.IsFolder()) return;

                    // The file will be overwritten if it exists on the server.
                    oUploadItem.SetOverwrite(true);
                });

                // Add all items to the upload queue.
                oUploadItemsCreated.Upload(oUploadItemsCreated.Items);
            },

            /* A user selected to skip existing files. */
            function onSkipExists() {

                // Create list of items that do not exist on the server.
                /** @type {ITHit.WebDAV.Client.Upload.UploadItem[]} aNotExistsUploadItems */
                var aNotExistsUploadItems = $.grep(oUploadItemsCreated.Items,
                    function (oUploadItem) {
                        return !ITHit.Utils.Contains(aExistsUploadItems, oUploadItem);
                    });

                // Add only items that do not exist on the server to the upload queue.
                oUploadItemsCreated.Upload(aNotExistsUploadItems);
            });
    }.bind(this));
};

/**
 * Verifies if each item in the list exists on the server and returns list of existing items.
 * @callback UploaderGridView~GetExistsAsyncCallback
 * @param {ITHit.WebDAV.Client.AsyncResult} oAsyncResult - The result of operation.
 * @param {ITHit.WebDAV.Client.Upload.UploadItem[]} oAsyncResult.Result - The array of items 
 * that exists on server.
 */

/**
 * @param {ITHit.WebDAV.Client.Upload.UploadItem[]} aUploadItems - Array of items to check.
 * @param {UploaderGridView~GetExistsAsyncCallback} fCallback - The function to be called when
 * all checks are completed.
 * @memberof UploaderGridView.prototype
 */
UploaderGridView.prototype._GetExistsAsync = function (aUploadItems, fCallback) {
    this._OpenItemsCollectionAsync(aUploadItems,
        function (aResultCollection) {
            var oFailedResult = ITHit.Utils.FindBy(aResultCollection,
                function (oResult) {
                    return !(oResult.AsyncResult.IsSuccess || oResult.AsyncResult.Status.Code === 404);
                },
                this);

            if (oFailedResult) {
                fCallback(oFailedResult.AsyncResult);
                return;
            }

            var aExistsItems = aResultCollection.filter(function (oResult) {
                return oResult.AsyncResult.IsSuccess;
            })
                .map(function (oResult) {
                    return oResult.UploadItem;
                });

            fCallback(new ITHit.WebDAV.Client.AsyncResult(aExistsItems, true, null));
        });

};

UploadItem.OnBeforeUploadStarted Event

UploadItem.OnBeforeUploadStarted is fired before file upload starts. Here you can do additional upload validation. For example if a network error or server error in Queue.OnUploadItemsCreated event handler occurred, you can validate file presence inside this event.

Similarly to the Queue.OnUploadItemsCreated event, the UploadItem.OnBeforeUploadStarted event provides parameter object, BeforeUploadStarted, that contains an UploadItem that is about to start uploading in the BeforeUploadStarted.Sender property. To start the upload you will call BeforeUploadStarted.Upload() function. 

You can force overwrite, set delete on cancel flag and set item error state using UploadItem.SetOverwrite(), UploadItem.SetDeleteOnCancel() and UploadItem.SetFailed() functions.

oUploadItem.AddListener('OnBeforeUploadStarted', this._OnBeforeUploadStarted, this);

/**
 * Called before item upload starts.
 * Here you can make additional checks and validation.
 * @param {ITHit.WebDAV.Client.Upload.UploadItem#event:OnBeforeUploadStarted} oBeforeUploadStarted 
 */
UploaderGridRow.prototype._OnBeforeUploadStarted = function (oBeforeUploadStarted) {

    // If the file does not exists on the server (verified when item was selected for upload) 
    // or it must be overwritten we start the upload.  
    /** @type {ITHit.WebDAV.Client.Upload.UploadItem} oItem */
    var oItem = oBeforeUploadStarted.Sender;
    if (oItem.GetOverwrite() || oItem.IsFolder() || oItem.CustomData.FileExistanceVerified) {
        oBeforeUploadStarted.Upload();
        return;
    }

    // Otherwise (item exitence verification failed, the server was down or network 
    // connection error orrured when item was selected for upload), 
    // below we verify that item does not exist on the server and upload can be started.
    var sHref = ITHit.EncodeURI(oItem.GetUrl());
    window.WebDAVController.WebDavSession.OpenItemAsync(sHref,
        [],
        function (oAsyncResult) {
            if (!oAsyncResult.IsSuccess && oAsyncResult.Status.Code === 404) {

                // The file does not exist on the server, start the upload.
                oBeforeUploadStarted.Upload();
                return;
            }

            if (!oAsyncResult.IsSuccess) {

                // An error during the request occured, do not upload file, set item error state.
                this.fileUploadFailedCallback(oAsyncResult.Error,
                    function () {
                        oBeforeUploadStarted.Sender.SetFailed(oAsyncResult.Error);
                    });

                return;
            }

            var sMessage = pasteFormat(sOverwriteDialogueFormat, oItem.GetRelativePath());

            // The file exists on the server, ask a user if it must be overwritten. 
            oConfirmModal.Confirm(sMessage,

                /* A user selected to overwrite existing file. */
                function onOverwrite() {

                    // Do not delete item if upload canceled (it existed before the upload).
                    oBeforeUploadStarted.Sender.SetDeleteOnCancel(false);

                    // The item will be overwritten if it exists on the server.
                    oBeforeUploadStarted.Sender.SetOverwrite(true);

                    // All async requests completed - start upload.
                    oBeforeUploadStarted.Upload();
                });

        }.bind(this));
};

Adding Custom Headers to File Upload

The functionality described in this section is provided in WebDAV Ajax Library v5.15 and later versions.

To add custom headers to each file upload you can use the UploadItem.AddHeader() function. For example, you can do it in UploadItem.OnBeforeUploadStarted event:

oUploadItem.AddListener('OnBeforeUploadStarted', this._OnBeforeUploadStarted, this);

UploaderGridRow.prototype._OnBeforeUploadStarted = function (oBeforeUploadStarted) {

    /** @type {ITHit.WebDAV.Client.Upload.UploadItem} oItem */
    var oItem = oBeforeUploadStarted.Sender;
    oItem.AddHeader('MyHeader','MyData'); 
    ...
};

Note that while HTTP standard and IT Hit Server Engine(s) does not have any limitations on header size, web servers and server frameworks may imply limitations on a single header size and all header size.

Upload Errors Processing and Restarting Upload

To analyze error returned by the server you will use the UploadItem.OnUploadError event. Inside this event, you can analyze error data returned by the server and show a message to the user. You can also automatically retry the upload from the next byte. The UploadError object passed as an event parameter provides access to the WebDavException via its UploadError.Error property. To Restart the upload from the next byte you will use the UploadError.Retry() function, to stop upload call the UploadError.Skip() function.

oUploadItem.AddListener('OnUploadError', this._OnUploadError, this);

/**
 * Called when upload error occurs.
 * @param {ITHit.WebDAV.Client.Upload.Events.UploadError} oUploadError - Contains 
 * WebDavException in UploadError.Error property as well as functions to restart the 
 * upload or stop the upload.
 */
UploaderGridRow.prototype._OnUploadError = function (oUploadError) {

    // Here you can verify error code returned by the server and show error UI, 
    // for example if server-side validation failed.

    // Stop upload if max upload retries reached.
    if (this._MaxRetry <= this._CurrentRetry) {
        this._ShowError(oUploadError.Error);
        oUploadError.Skip();
        return;
    }

    // Retry upload.
    var retryTime = (new Date()).getTime() + (this._RetryDelay * 1000);
    var retryTimerId = setInterval(function () {
        var timeLeft = retryTime - (new Date()).getTime();
        if (timeLeft > 0) {
            this._SetRetryMessage(timeLeft);
            return;
        }
        clearInterval(retryTimerId);
        this._CurrentRetry++;
        this._RemoveRetryMessage();

        // Request number of bytes succesefully saved on the server 
        // and retry upload from next byte.
        oUploadError.Retry();

    }.bind(this), 1000);
    this.CancelRetryCallback = function () {
        clearInterval(retryTimerId);
        this._RemoveRetryMessage();
    }
};

 

Next Article:

Managing Items Hierarchy with JavaScript WebDAV Library