How to Build High‑Performance Large File HTTP Uploads with Resume Support

This article outlines a practical approach for implementing high‑performance large file HTTP uploads with breakpoint resume, covering server‑side C implementation, client‑side hash generation, progress tracking, chunked uploads using HTML5 File.slice, and detailed JavaScript code examples for robust file transfer.

Java Interview Crash Guide
Java Interview Crash Guide
Java Interview Crash Guide
How to Build High‑Performance Large File HTTP Uploads with Resume Support

Recently, due to product requirements of the R&D group, we needed to support high‑performance large‑file HTTP uploads with breakpoint resume. Below is a concise summary for easy reference.

Server side is implemented in C rather than interpreted languages like Java or PHP.

Server writes directly to disk, avoiding functions such as move_uploaded_file or InputStreamReader that would require buffering and could cause memory usage or timeout.

Support HTML5 and IFRAME (for older browsers) and provide upload progress.

Because mobile internet is unstable, supporting breakpoint resume and reconnection is essential to avoid re‑uploading large files after a disconnection.

Breakpoint‑resume concept: The client (usually a browser) uploads a file while continuously recording progress; if a disconnection occurs, the client can query the server for the already uploaded size and continue from that offset.

Some solutions split the file into small chunks (e.g., 4 MB) and merge them on the server after all parts arrive. This works for small files but becomes slow for hundreds of megabytes or gigabytes.

1. Generate a file HASH in the browser

Do not rely solely on the file name; combine browser‑generated ID, file modification time, name, and size. The ideal hash is an MD5 of the file content, but that is costly, so a lighter approach is used.

Assign a unique ID to the browser and store it in a cookie.

Compute MD5 of the concatenated string: browser ID + file lastModified + file name + file size.

The browser ID is automatically granted when the browser accesses the upload site.

function setCookie(cname,cvalue,exdays){
  var d = new Date();
  d.setTime(d.getTime()+(exdays*24*60*60*1000));
  var expires = "expires="+d.toGMTString();
  document.cookie = cname + "=" + cvalue + "; " + expires;
}

function getCookie(cname){
  var name = cname + "=";
  var ca = document.cookie.split(';');
  for(var i=0; i<ca.length; i++) {
    var c = ca[i].trim();
    if (c.indexOf(name)==0) return c.substring(name.length,c.length);
  }
  return "";
}

function getFileId(file){
  var clientid = getCookie("HUAYIUPLOAD");
  if (clientid == "") {
    var rand = parseInt(Math.random() * 1000);
    var t = (new Date()).getTime();
    clientid = rand + 'T' + t;
    setCookie("HUAYIUPLOAD",clientid,365);
  }
  var info = clientid;
  if (file.lastModified) info += file.lastModified;
  if (file.name) info += file.name;
  if (file.size) info += file.size;
  var fileid = md5(info);
  return fileid;
}

The author notes that reading the file content to compute a hash is very slow; the lightweight method is sufficient unless true HTTP instant‑transfer is required.

2. Query the file HASH

After obtaining the file ID, request the server for the uploaded size and resume from that offset.

var fileObj = currentfile;
var fileid = getFileId(fileObj);
var t = (new Date()).getTime();
var url = resume_info_url + '?fileid=' + fileid + '&t=' + t;
var ajax = new XMLHttpRequest();
ajax.onreadystatechange = function(){
  if(this.readyState == 4){
    if(this.status == 200){
      var response = this.responseText;
      var result = JSON.parse(response);
      if(!result){
        alert('Server returned invalid data, possibly incompatible.');
        return;
      }
      var uploadedBytes = result.file && result.file.size;
      if(!result.file.finished && uploadedBytes < fileObj.size){
        upload_file(fileObj, uploadedBytes, fileid);
      } else {
        showUploadedFile(result.file);
      }
    } else {
      alert('Failed to get resume information');
    }
  }
};
ajax.open('get', url, true);
ajax.send(null);

3. Perform the upload

Use HTML5 File.slice to upload chunks starting from the previously uploaded offset.

Key API: File.slice(start, end) extracts a portion of the file and returns it as a new Blob.

Parameters:

start : start index of the slice (negative values count from the end).

end : end index (optional; if omitted, slice goes to the file end).

function upload_file(fileObj,start_offset,fileid){
  var xhr = new XMLHttpRequest();
  var formData = new FormData();
  if(start_offset >= fileObj.size){
    return false;
  }
  var bitrateDiv = document.getElementById("bitrate");
  var finishDiv = document.getElementById("finish");
  var progressBar = document.getElementById('progressbar');
  var progressDiv = document.getElementById('percent-label');
  var oldTimestamp = 0;
  var oldLoadsize = 0;
  var totalFilesize = fileObj.size;
  if(totalFilesize == 0) return;
  var uploadProgress = function(evt){
    if(evt.lengthComputable){
      var uploadedSize = evt.loaded + start_offset;
      var percentComplete = Math.round(uploadedSize * 100 / totalFilesize);
      var timestamp = (new Date()).valueOf();
      var isFinish = evt.loaded == evt.total;
      if(timestamp > oldTimestamp || isFinish){
        var duration = timestamp - oldTimestamp;
        if(duration > 500 || isFinish){
          var size = evt.loaded - oldLoadsize;
          var bitrate = (size * 8 / duration / 1024) * 1000; // kbps
          if(bitrate > 1000) bitrate = Math.round(bitrate/1000) + 'Mbps';
          else bitrate = Math.round(bitrate) + 'Kbps';
          var finish = evt.loaded + start_offset;
          if(finish > 1048576)
            finish = (Math.round(finish / (1048576/100)) / 100).toString() + 'MB';
          else
            finish = (Math.round(finish / (1024/100)) / 100).toString() + 'KB';
          progressBar.value = percentComplete;
          progressDiv.innerHTML = percentComplete + '%';
          bitrateDiv.innerHTML = bitrate;
          finishDiv.innerHTML = finish;
          oldTimestamp = timestamp;
          oldLoadsize = evt.loaded;
        }
      }
    } else {
      progressDiv.innerHTML = 'N/A';
    }
  };
  xhr.onreadystatechange = function(){
    if(xhr.readyState == 4 && xhr.status == 200){
      console.log(xhr.responseText);
    } else if(xhr.status == 400){
      // handle error
    }
  };
  var uploadComplete = function(evt){
    progressDiv.innerHTML = '100%';
    var result = JSON.parse(evt.target.responseText);
    if(result.result == 'success'){
      showUploadedFile(result.files[0]);
    } else {
      alert(result.msg);
    }
  };
  var uploadFailed = function(){
    alert('Upload failed!');
  };
  var uploadCanceled = function(){
    alert('Upload canceled or connection lost!');
  };
  xhr.overrideMimeType('application/octet-stream');
  var filesize = fileObj.size;
  var blob = fileObj.slice(start_offset, filesize);
  var fileOfBlob = new File([blob], fileObj.name);
  formData.append('filename', fileObj.name);
  formData.append('fileid', fileid);
  formData.append('file', fileOfBlob);
  xhr.upload.addEventListener('progress', uploadProgress, false);
  xhr.addEventListener('load', uploadComplete, false);
  xhr.addEventListener('error', uploadFailed, false);
  xhr.addEventListener('abort', uploadCanceled, false);
  xhr.open('POST', upload_file_url);
  xhr.send(formData);
}

A simple UI displays progress, uploaded size, bitrate, and handles errors or cancellations.

By querying the server for the already uploaded size and using File.slice, the client can resume uploads without re‑sending data that has already been transferred.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaScriptresumelarge filesC serverHTTP upload
Java Interview Crash Guide
Written by

Java Interview Crash Guide

Dedicated to sharing Java interview Q&A; follow and reply "java" to receive a free premium Java interview guide.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.