title: Upload properly encoded (base64?) image to SharePoint with AngularJS
body: <p>I am able to upload an image file to SharePoint, but it is not being recognized as an image. I have tried utilizing the following directive based on research that states images need to be base64 encoded when uploaded into SharePoint, but it still uploads a file that appears to be corrupt: <a href="https://github.com/adonespitogo/angular-base64-upload">https://github.com/adonespitogo/angular-base64-upload</a> </p>
  
  <p>I am happy to use this directive, but am unsure of how to pass what I need into SharePoint's REST API.</p>
  
  <p>The original iteration I had does not use this directive, but is more of a straight upload attempt. </p>
  
  <p>What I need to achieve is as follows:</p>
  
  <p>1) Successfully upload an image without it being "corrupted", and does this require base64 encoding/how do I achieve this?</p>
  
  <p>2) Upload images by their name (not "test.jpg") and have some metadata (ex. upload an image with the title or department name it belongs to)</p>
  
  <p><strong>Iteration 1: No Directive</strong>
  Here is my HTML (Please note that the controller is tied to the page via ng-route):</p>
  
  <pre><code> &lt;div class="col-md-12"&gt;
          &lt;form&gt;
              &lt;input type="file" onchange="angular.element(this).scope().filesChanged(this)" data-ng-model="files" multiple&gt;
              &lt;button data-ng-click="upload()"&gt;Submit&lt;/button&gt;
              &lt;li data-ng-repeat="file in files"&gt;{{file.name}}&lt;/li&gt;
          &lt;/form&gt;            
      &lt;/div&gt;
  </code></pre>
  
  <p>Here is my controller:</p>
  
  <pre><code>$scope.filesChanged = function (elm) {
          $scope.files = elm.files
          $scope.$apply();
      }
      $scope.upload = function () {
          var fd = new FormData()
          angular.forEach($scope.files,function(file){
              fd.append('file',file)
          })
              $http.post("/sites/asite/_api/web/lists/getByTitle('Images')/RootFolder/Files/add(url='test.jpg',overwrite='true')", fd,
                      {
                          transformRequest: angular.identity,
                          headers: {
                              'Content-Type':undefined, 'X-RequestDigest': $("#__REQUESTDIGEST").val()}
                  }).success(function (d) {
                      console.log(d);
                  });
              }
  </code></pre>
  
  <p><strong>UPDATE:</strong> I believe the issue is isolated to my $http post to SharePoint. Using the directive mentioned above, I am able to output the base64, but am unsure how to pass this into my post for upload. </p>
  
  <p><strong>Iteration 2: Using Directive</strong>
  Here is my current HTML using the <a href="https://github.com/adonespitogo/angular-base64-upload">https://github.com/adonespitogo/angular-base64-upload</a> directive:</p>
  
  <pre><code>&lt;form&gt;
  &lt;input type="file" data-ng-model="files" base-sixty-four-input&gt;
  &lt;button data-ng-click="upload()"&gt;Submit&lt;/button&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>My controller that is posting the corrupted image files to SharePoint:</p>
  
  <pre><code>$scope.upload = function () {
          console.log($scope.files); // Output result from upload directive
          $http({
              method: 'POST',
              url: "/sites/ens/_api/web/lists/getByTitle('Report Images')/RootFolder/Files/add(url='" + $scope.files.filename +"',overwrite='true')", 
              headers: {
                  'Content-Type': false ,
                  'X-RequestDigest': $("#__REQUESTDIGEST").val()
              },
              data: $scope.files,
           }).success(function (data) {
              console.log(data);
          });
      }
  </code></pre>
  
  <p><strong>Update 2:</strong> Using SP.RequestExecutor as follows creates the same result. A file upload but not rendering. This happens for images and documents:</p>
  
  <p><strong>Iteration 3: Using Directive and SP.RequestExecutor</strong></p>
  
  <pre><code>$scope.upload = function () {
      var dataURL = 'data:' + $scope.files.filetype + ';' + 'base64,' + $scope.files.base64;
      var createitem = new SP.RequestExecutor("/sites/asite");
      createitem.executeAsync({
          url: "/sites/asite/_api/web/lists/getByTitle('Images')/RootFolder/Files/add(url='" + $scope.files.filename + "')",
          method: "POST",
          binaryStringRequestBody: true,
          body: dataURL,
          success: fsucc,
          error: ferr,
          state: "Update"
      });
  
      function fsucc(data) {
          alert('success');
      }
      function ferr(data) {
          alert('error\n\n' + data.statusText + "\n\n" + data.responseText);
      }
  }
  </code></pre>
  
  <p><strong>Update 3:</strong> Using .ajax as follows, it will successfully post the image, but when using $http, it corrupts the image.</p>
  
  <p>Iteration 3: Using .Ajax (works)</p>
  
  <pre><code>function uploadFileSync(spWebUrl , library, filename, file) 
      {
          var reader = new FileReader();
          reader.onloadend = function(evt) 
          {
              if (evt.target.readyState == FileReader.DONE) 
              {
                  var buffer = evt.target.result;
                  var completeUrl = spWebUrl
                    + "/_api/web/lists/getByTitle('"+ library +"')"
                    + "/RootFolder/Files/add(url='"+ filename +"',overwrite='true')?"
                    + "@TargetLibrary='"+library+"'&amp;@TargetFileName='"+ filename +"'";
  
                  $.ajax({
                      url: completeUrl,
                      type: "POST",
                      data: buffer,
                      async: false,
                      processData: false,
                      headers: {
                          "accept": "application/json;odata=verbose",
                          "X-RequestDigest": $("#__REQUESTDIGEST").val(),
                          "content-length": buffer.byteLength
                      },
                      complete: function (data) {
                          //uploaded pic url
                          console.log(data.responseJSON.d.ServerRelativeUrl);
                          $route.reload();
                      },
                      error: function (err) {
                          alert('failed');
                      }
                  });
  
              }
          };
          reader.readAsArrayBuffer(file);
      }  
  </code></pre>
  
  <p>Iteration 4: Using $http (corrupts image)</p>
  
  <pre><code>function uploadFileSync(spWebUrl , library, filename, file) 
  {
      var reader = new FileReader();
      reader.onloadend = function (evt) {
          if (evt.target.readyState == FileReader.DONE) {
              var buffer = evt.target.result;
              var completeUrl = spWebUrl
                + "/_api/web/lists/getByTitle('" + library + "')"
                + "/RootFolder/Files/add(url='" + filename + "',overwrite='true')?"
                + "@TargetLibrary='" + library + "'&amp;@TargetFileName='" + filename + "'";
  
              $http({
                  url: completeUrl,
                  method: "POST",
                  data: buffer,
                  processData: false,
                  headers: {
                      "accept": "application/json;odata=verbose",
                      "X-RequestDigest": $("#__REQUESTDIGEST").val(),
                      "content-length": buffer.byteLength
                  }
              }).success(function (data) {
                  //uploaded pic url
                  //console.log(data.responseJSON.d.ServerRelativeUrl);
                  $route.reload();
              }).error(function (err) {
                  alert(err);
              });
          }
      };
      reader.readAsArrayBuffer(file);
  }
  </code></pre>