title: Uploading Large Files with Ajax call
body: <p>I am trying to use the Microsoft Graph API to upload large files using an Ajax call.</p>
  
  <p>According to the <a href="https://docs.microsoft.com/en-us/onedrive/developer/rest-api/api/driveitem_createuploadsession" rel="nofollow noreferrer">documentation</a>, you first have to create an upload session which I can do successfully with my code.  The problem comes when I start my upload to the returned <code>uploadUrl</code>.  I get the following error:</p>
  
  <pre class="lang-json prettyprint-override"><code>{
      code: "invalidRequest",
      message: "The Content-Range header length does not match the provided number of bytes."
  }
  </code></pre>
  
  <p>So when I check the actual request in Fiddler, I can see that the <code>Content-Length</code> header is set to <code>0</code>.</p>
  
  <p>So I tried setting my <code>Content-Length</code> header to the size of the <code>ArrayBuffer</code> that I'm sending, but I get an error (Chrome) that says: </p>
  
  <p><code>Refused to set unsafe header "Content-Length"</code></p>
  
  <p>I've been struggling with this for 2 full days now and I'm at my wit's end.  There is very little documentation on the Microsoft Graph API, and even fewer examples that seem to fit what I'm trying to do.  </p>
  
  <p>I can't imagine I'm the only one out there that's attempting to do this, I would think it would be a fairly common idea?</p>
  
  <p>Below is the code I'm using.  I'm getting my <code>AccessToken</code> and <code>URL</code> elsewhere, but they seem to be fine as I can query using them from the console.</p>
  
  <pre class="lang-js prettyprint-override"><code>this.UploadLargeFileToFolderID = function (FolderID,
      FileObject,
      ShowLoadingMessage,
      SuccessFunction,
      ErrorFunction,
      CompleteFunction) { //will upload a file up to 60Mb to folder.
  
      //shows the loading messag
      ShowLoadingMessage &amp;&amp; ThisRepository.LoadingMessage.Show();
  
      //cleans the file name to something acceptable to SharePoint
      FileObject.name = CleanOneDriveFileName(FileObject.name);
  
      var UploadSessionURL = FolderID ?
          ThisRepository.RepositoryRootURL + '/drive/items/' + FolderID + '/createUploadSession' :
          ThisRepository.RepositoryRootURL + '/drive/root/createUploadSession';
  
      //First, get the Upload Sesion.
      $.ajax({
          url: UploadSessionURL,
          method: 'POST',
          headers: {
              authorization: "Bearer " + ThisRepository.AccessToken
          },
          success: function (data, textStatus, jqXHR) {
              //successfully got the upload session.            
              console.log('Session:');
              console.log(data);
  
              //Create the ArrayBuffer and upload the file.
              ReturnArrayBufferFromFile(FileObject, function (ArrayBuffer) {
                  console.log('Array Buffer:');
                  console.log(ArrayBuffer);
                  var MaxChunkSize = 327680;
                  var ChunkSize = ArrayBuffer.byteLength &lt; MaxChunkSize ?
                      ArrayBuffer.byteLength :
                      MaxChunkSize;
  
                  chunkedUpload(data.uploadUrl, ArrayBuffer, ChunkSize, 0, null,
                      null, null, null,
                      function (response) {
                          console.log(response);
                          !SuccessFunction &amp;&amp; console.log(response);
                          typeof SuccessFunction === 'function' &amp;&amp; SuccessFunction(response);
                      });
  
              });
  
          },
          error: function (jqXHR, textStatus, errorThrown) {
              console.log(jqXHR);
              typeof ErrorFunction === 'function' &amp;&amp; ErrorFunction(jqXHR);
          },
          complete: function (jqXHR, textStatus) {
              ThisRepository.LoadingMessage.Remove();
              typeof CompleteFunction === 'function' &amp;&amp; CompleteFunction(jqXHR);
          },
      });
  
  };
  </code></pre>
  
  <p>Function for returning the Array Buffer to send </p>
  
  <pre class="lang-js prettyprint-override"><code>function ReturnArrayBufferFromFile(InputFile, CallBackFunction) {
      console.log('Input File');
      console.log(InputFile);
      var FileName = CleanOneDriveFileName(InputFile.name);
      var FileUploadReader = new FileReader();
  
      if (InputFile.type.match('image.*')) {
          // if the file is an image, we want to make sure 
          // it's not too big before we return it.
          FileUploadReader.onloadend = function (e) {
              var img = new Image();
  
              //will resize an image to a maximum of 2 megapixels.
              img.onload = function () {
                  var MAX_HEIGHT = 2048; //max final height, in pixels
                  var MAX_WIDTH = 2048; //max final width, in pixels
                  var height = img.height;
                  var width = img.width;
  
                  //do the resizing
                  if (width &gt; height) { //landscape image
                      if (width &gt; MAX_WIDTH) {
                          height *= MAX_WIDTH / width;
                          width = MAX_WIDTH;
                      };
                  } else { //portrait image
                      if (height &gt; MAX_HEIGHT) {
                          width *= MAX_HEIGHT / height;
                          height = MAX_HEIGHT;
                      };
                  };
  
                  //Create a new canvas element, correctly sized with the image
                  var canvas = document.createElement("canvas");
                  canvas.width = width;
                  canvas.height = height;
                  canvas.getContext('2d').drawImage(this, 0, 0, width, height);
  
                  //Create the new file reader for the upload function.                   
                  var ConvertedFile = canvas.toBlob(function (blob) {
                      var ConvertedFileReader = new FileReader();
  
                      ConvertedFileReader.onloadend = function (loadendevent) {
                          //return loadendevent.target.result;
                          var result = loadendevent.target.result;
                          var Rawresult = result.split(',')[1];
                          CallBackFunction(loadendevent.target.result);
                      };
  
                      ConvertedFileReader.readAsArrayBuffer(blob);
  
                  }, 'image/jpeg', 0.90);
              };
  
              img.src = e.target.result;
          };
  
          FileUploadReader.readAsArrayBuffer(InputFile);
      } else {
          //File is not an image.  No pre-work is required.  Just upload it.
          FileUploadReader.onloadend = function (e) {
              CallBackFunction(e.target.result);
          };
          FileUploadReader.readAsArrayBuffer(InputFile);
      };
  };
  </code></pre>
  
  <p>And finally, the chunkUpload function:</p>
  
  <pre class="lang-js prettyprint-override"><code>function chunkedUpload(url, file, chunkSize, chunkStart,
      chunkEnd, chunks, chunksDone, fileChunk, CompleteCallBack) {
  
      var filesize = file.byteLength;
  
      chunkSize = chunkSize ? chunkSize : 327680;
      chunkStart = chunkStart ? chunkStart : 0;
      chunkEnd = chunkEnd ? chunkEnd : chunkSize;
      chunks = chunks ? chunks : filesize / chunkSize;
      chunksDone = chunksDone ? chunksDone : 0;
      fileChunk = fileChunk ? fileChunk : file.slice(chunkStart, chunkEnd);
  
      var req = new XMLHttpRequest();
  
      req.open("PUT", url, true);
      //req.setRequestHeader("Content-Length", file.size.toString());
      req.setRequestHeader("Content-Range", "bytes " + chunkStart + "-" +
          (chunkEnd - 1) + "/" + filesize);
  
      req.onload = (e) =&gt; {
              let response = JSON.parse(req.response);
              console.log(response);
              if (response.nextExpectedRanges) {
                  let range = response.nextExpectedRanges[0].split('-'),
                      chunkStart = Number(range[0]),
                      nextChunk = chunkStart + chunkSize,
                      chunkEnd = nextChunk &gt; file.byteLength ? file.byteLength : nextChunk;
                  console.log(((chunksDone++/ chunks) * 100), '%' );
                              chunkedUpload(url, file, chunkSize, chunkStart,
                                  chunkEnd, chunks, chunksDone, CompleteCallBack);
                          }
                          else {
                              console.log("upload session complete");
                              typeof CompleteCallBack === 'function' &amp;&amp;
                                  CompleteCallBack(response);
                          }
                      }; req.send(file);
                  }
  </code></pre>