AddAttachment

Feb 7, 2013 at 4:46 PM
Edited Feb 7, 2013 at 7:06 PM
I'm creatig a form with plain HTML and jQuery/SPSerivces. The form has a few fields for file uploads. This is all well and good. On my submit function, I'm successfully adding an item and I get back the ID of the associated newly created list item.

I'm in turn passing that ID into the AddAttachment operation. My issue is how do I fetch the file and stream it to Sharepoint? mI'm not finding anything helpful on this, the most important part.

If there was something in the CSOM that would be beneficial that would work to. I'm just not getting how to take the input fields and convert the files into byte arrays.
Feb 7, 2013 at 8:53 PM
Have you read over this thread?
http://spservices.codeplex.com/discussions/221435?ProjectName=spservices

I too have had issues with other file types, but then again, I may be doing something wrong. I have not tried it using JSOM, so that API very well may be more robust.

Cheers,
Matthew
Feb 7, 2013 at 9:08 PM
I did see that thread. Unfortunately I can't rely on ActiveX, there are too many Chrome and Firefox users I have to account for.

There is a SP.Base64EncodedByteArray() method in the JSOM, but I can't seem to figure out how to use it with the input coming from an input type="file" field.
Feb 7, 2013 at 9:30 PM
Edited Feb 7, 2013 at 9:31 PM
I noticed that method just a month ago while helping my colleague with this script. I've confirmed this works with .csv's and .txt files. Although, this doesn't answer your input type='file' question, it is production code. :)

YMMV
<div>
    <p> testing attaching a csv file to a SP List item
    <input type='button' value="Attach File Test" onclick='javascript:attachFileText();' />
    <br />

    <div id="output" style="border: solid 1px #000000;"></div>
</div>

<!-- Change file paths below to where you are hosting your jquery files -->
<script src='/hr/SiteAssets/jquery-1.8.3.min.js' type='text/javascript'></script>
<script src='/hr/SiteAssets/jquery.SPServices-0.7.2.min.js' type='text/javascript'></script>
<script >

    // Took JS from:
    // http://spservices.codeplex.com/
    // http://spservices.codeplex.com/discussions/221435
    // http://spservices.codeplex.com/wikipage?title=Lists&referringTitle=%24%28%29.SPServices
    // Tested on SharePoint online site
    
    // Sample JS Method to attach file to a list item that already exists
    function attachFileText()
    {
      // for brevity, left out code that builds csvData...

      var fileContent = csvData;
      var SPwebURL = "http://client.sharepoint.com/"; // optional 
      var SPlistName = "History";
      var id = 236; // picked an arbitrary List ID value
      // optional argument: webURL: SPwebURL,
      $().SPServices({
         operation: "AddAttachment",
         listName: SPlistName,
         listItemID: id,
         fileName: 'logs.csv',
         attachment: Base64.encode(fileContent),
         async: false,
         completefunc: function(xData, Status)
         {
          // log the data to the console
          console.log(xData.responseText);
          // or write some output to our html page
          $('#output').html("done! + status = " + Status + ", message = " + xData.responseText);
         }
       });
    }

    // Routines to encode file contents so that the SOAP request stays in a valid XML format
    // Javascript base64
    // http://www.webtoolkit.info/javascript-base64.html
    
var Base64 = {
  // private property
  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

  // public method for encoding
  encode : function (input) {
          var output = "";
          var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
          var i = 0;
          input = Base64._utf8_encode(input);
          while (i < input.length) {
                  chr1 = input.charCodeAt(i++);
                  chr2 = input.charCodeAt(i++);
                  chr3 = input.charCodeAt(i++);

                  enc1 = chr1 >> 2;
                  enc2 = ((chr1 & 3) << 4) | (chr2 >> 4);
                  enc3 = ((chr2 & 15) << 2) | (chr3 >> 6);
                  enc4 = chr3 & 63;

                  if (isNaN(chr2)) {
                          enc3 = enc4 = 64;
                  } else if (isNaN(chr3)) {
                          enc4 = 64;
                  }

                  output = output +
                  this._keyStr.charAt(enc1) + this._keyStr.charAt(enc2) +
                  this._keyStr.charAt(enc3) + this._keyStr.charAt(enc4);
          }

          return output;
  },

  // public method for decoding
  decode : function (input) {
          var output = "";
          var chr1, chr2, chr3;
          var enc1, enc2, enc3, enc4;
          var i = 0;
          input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
          while (i < input.length) {

                  enc1 = this._keyStr.indexOf(input.charAt(i++));
                  enc2 = this._keyStr.indexOf(input.charAt(i++));
                  enc3 = this._keyStr.indexOf(input.charAt(i++));
                  enc4 = this._keyStr.indexOf(input.charAt(i++));

                  chr1 = (enc1 << 2) | (enc2 >> 4);
                  chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
                  chr3 = ((enc3 & 3) << 6) | enc4;

                  output = output + String.fromCharCode(chr1);

                  if (enc3 != 64) {
                          output = output + String.fromCharCode(chr2);
                  }
                  if (enc4 != 64) {
                          output = output + String.fromCharCode(chr3);
                  }

          }

          output = Base64._utf8_decode(output);
          return output;
  },

  // private method for UTF-8 encoding
  _utf8_encode : function (string) {
          string = string.replace(/\r\n/g,"\n");
          var utftext = "";
          for (var n = 0; n < string.length; n++) {
                  var c = string.charCodeAt(n);

                  if (c < 128) {
                          utftext += String.fromCharCode(c);
                  }
                  else if((c > 127) && (c < 2048)) {
                          utftext += String.fromCharCode((c >> 6) | 192);
                          utftext += String.fromCharCode((c & 63) | 128);
                  }
                  else {
                          utftext += String.fromCharCode((c >> 12) | 224);
                          utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                          utftext += String.fromCharCode((c & 63) | 128);
                  }

          }

          return utftext;
  },

  // private method for UTF-8 decoding
  _utf8_decode : function (utftext) {
          var string = "";
          var i = 0;
          var c = c1 = c2 = 0;
          while ( i < utftext.length ) {
                  c = utftext.charCodeAt(i);
                  if (c < 128) {
                          string += String.fromCharCode(c);
                          i++;
                  }
                  else if((c > 191) && (c < 224)) {
                          c2 = utftext.charCodeAt(i+1);
                          string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                          i += 2;
                  }
                  else {
                          c2 = utftext.charCodeAt(i+1);
                          c3 = utftext.charCodeAt(i+2);
                          string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                          i += 3;
                  }
          }
          return string;
  }
}

</script>
Cheers,
Matthew
Feb 14, 2013 at 1:14 PM
Matthew,
In your script, you commented "for brevity, left out code that builds csvData..." I'm guessing you're building a string object and then using the "Base64.encode" function to convert that to a file attachemnt object?  Will this method work if the source is not a string but rather a file.  I have experimented with uploading file objects into a SharePoint document library using VBA and that all works quite well but haven't been able to find similar functionality using JavaScript.  Your code above is the closest I've seen to a solution but I don't think it quite gets me there.  Are you aware of a similar technique for uploading a file from a local drive to a SharePoint document library?  I saw something about using an iFrames object to house/control an Upload.aspx page but I'm not real fond of that method.
Thanks,,

Geoff
Feb 14, 2013 at 3:19 PM
I'm with you on the iFrames hack. There is talk right now about knocking this functionality out of the park. I have not done so to date, but I know it's being worked on. Once I do it or know of someone who has, I'll update this thread. My hope is to accomplish this soon, so there's that. :-)

The code does manipulate a string value and then encodes it, so essentially, the file's data was built on the fly.

Cheers,
Matthew