3

I am trying to upload list item attachment using the below code. There is no error in the code and all the variables are present with proper values but in uploadFile method it goes to error handler and says File not found. Can anyone help?

function UploadAttachment() {
 this.filename = $('input[type=file]').val().split('\\').pop();
 var args = window.frameElement.dialogArgs;
 this.itemId = args.ItemId; //$.QueryString["ItemId"];
 this.clientContext = new SP.ClientContext.get_current();
 var outReachList = clientContext.get_web().get_lists().getByTitle('Outreach List');
 this.listItem = outReachList.getItemById(itemId);
 clientContext.load(listItem);
 clientContext.executeQueryAsync(Function.createDelegate(this, this.HasListItemAttachment), Function.createDelegate(this, this.GetAllAttachmentsFailed));
}

function HasListItemAttachment() {
 var attachmentFolderUrl = String.format('{0}/Attachments/{1}',listItem.get_fieldValues()['FileDirRef'], itemId);
 console.log(attachmentFolderUrl);
 this.attachmentFolder = clientContext.get_web().getFolderByServerRelativeUrl(attachmentFolderUrl);
 clientContext.load(attachmentFolder);
 clientContext.executeQueryAsync(Function.createDelegate(this, this.uploadFile), Function.createDelegate(this, GetAllAttachmentsFailed));
}

//function to actually upload file
function uploadFile() {
 var file = document.getElementById("customFileUploadControl").files[0];
 getFileBuffer(file).then( function(buffer) {
 var bytes = new Uint8Array(buffer);
 var content = new SP.Base64EncodedByteArray(); //base64 encoding
 for (var b = 0; b < bytes.length; b++) {
     content.append(bytes[b]);
  }
  var createInfo = new SP.FileCreationInformation();
  createInfo.set_content(content); //setting the content of the new file
  createInfo.set_url(filename);
  attachmentFolder.add(createInfo);
  clientContext.executeQueryAsync(Function.createDelegate(this, uploadFileSuccess), Function.createDelegate(this, GetAllAttachmentsFailed));
 });
}

function uploadFileSuccess() {
SP.UI.ModalDialog.commonModalDialogClose(SP.UI.DialogResult.OK);
 }

function GetAllAttachmentsFailed(sender, args) {
console.log('Request failed. ' + args.get_message() + '\n' + args.get_stackTrace());
 }

 function getFileBuffer(file){
var deferred = $.Deferred();
var reader = new FileReader();
reader.onload = function(e){
    deferred.resolve(e.target.result);
}
reader.onerror = function(e){
    deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(file);
return deferred.promise();
 }
4
  • did you tried SPServices sympmarc.com/2014/05/27/…? Commented Mar 27, 2015 at 7:02
  • no I didn't. I am interested to know why this is not working before moving on to other approaches.
    – Garima
    Commented Mar 27, 2015 at 7:05
  • are you using Ajax/Update panel in page? Commented Mar 27, 2015 at 7:12
  • no. just normal html file upload control and a submit button. on click calling this method.
    – Garima
    Commented Mar 27, 2015 at 7:59

3 Answers 3

6

Most probably the error File not found occurs since you are trying to retrieve attachments folder that not yet exist in method HasListItemAttachment

The following example demonstrates how to upload attachment file via SharePoint JSOM:

function processUpload(fileInput, listTitle, itemId,success,error) {
    var reader = new FileReader();
    reader.onload = function (result) {
        var fileContent = new Uint8Array(result.target.result);
        performAttachmentUpload(listTitle, fileInput.name, itemId, fileContent,success,error);
    };
    reader.readAsArrayBuffer(fileInput);
}


function performAttachmentUpload(listTitle, fileName, itemId, fileContent,success,error) {

    ensureAttachmentFolder(listTitle,itemId, 
       function(folder){
           var attachmentFolderUrl = folder.get_serverRelativeUrl();
           uploadFile(attachmentFolderUrl,fileName,fileContent,success,error);
       },
       error);
}


function ensureAttachmentFolder(listTitle,itemId, success,error)
{
      var ctx = SP.ClientContext.get_current();
      var web = ctx.get_web();
      var list = web.get_lists().getByTitle(listTitle);
      ctx.load(list,'RootFolder');
      var item = list.getItemById(itemId);
      ctx.load(item);
      ctx.executeQueryAsync(
        function() {
            var attachmentsFolder;
            if(!item.get_fieldValues()['Attachments']) { /* Attachments folder exists? */
               var attachmentRootFolderUrl = String.format('{0}/Attachments',list.get_rootFolder().get_serverRelativeUrl()); 
               var attachmentsRootFolder = ctx.get_web().getFolderByServerRelativeUrl(attachmentRootFolderUrl);
               //Note: Here is a tricky part. 
               //Since SharePoint prevents the creation of folder with name that corresponds to item id, we are going to:   
               //1)create a folder with name in the following format '_<itemid>'
               //2)rename a folder from '_<itemid>'' into '<itemid>'
               //This allow to bypass the limitation of creating attachment folders
               attachmentsFolder = attachmentsRootFolder.get_folders().add('_' + itemId);
               attachmentsFolder.moveTo(attachmentRootFolderUrl + '/' + itemId);
               ctx.load(attachmentsFolder);
            }
            else {
               var attachmentFolderUrl = String.format('{0}/Attachments/{1}',list.get_rootFolder().get_serverRelativeUrl(),itemId); 
               attachmentsFolder = ctx.get_web().getFolderByServerRelativeUrl(attachmentFolderUrl);
               ctx.load(attachmentsFolder);
            }         
            ctx.executeQueryAsync(
                 function() {
                     success(attachmentsFolder); 
                 },
                 error);
        },
        error);
}


function uploadFile(folderUrl,fileName,fileContent,success,error)
{
     var ctx = SP.ClientContext.get_current();
     var folder = ctx.get_web().getFolderByServerRelativeUrl(folderUrl);
     var encContent = new SP.Base64EncodedByteArray(); 
     for (var b = 0; b < fileContent.length; b++) {
         encContent.append(fileContent[b]);
     }
     var createInfo = new SP.FileCreationInformation();
     createInfo.set_content(encContent); 
     createInfo.set_url(fileName);
     folder.get_files().add(createInfo);
     ctx.executeQueryAsync(success,error);
}

Note: It has been tested in SharePoint 2013/SPO

Usage

var listTitle = 'Contacts';
var itemId = 1;
var fileInput = document.getElementById("customFileUploadControl");
var file = fileInput.files[0];
processUpload(file,  listTitle, itemId,
  function(){
     console.log('Attachment file has been uploaded');  
  }, 
  function(sender,args){
     console.log(args.get_message());
  });
2
  • 1
    attachmentsFolder.moveTo() does not seem to work for me, function does not exist
    – elsni
    Commented Nov 23, 2016 at 8:03
  • 1
    Thank you very much for this code. It works fine, but it is missing some important part: when I remove the last file from an item, it seems that the field is unset but the folder still exists, what causes the routine to crash because of moveTo trying to override a file. Would you mind give me a hint how to solve this? I tried checking for existence but my code isn't working due to a lack of understanding the SP api, I guess.
    – K. D.
    Commented Sep 17, 2022 at 12:01
1

I am a little late to the party, but when I tried the code above I too got the same error as "Elsni" (See below error message). I am not sure why there is a comment in the code saying you can not create attachment folders in order to get the code sample above to work I simply did this (see code below). Is this a SPO thing? I am using SP2013 on-prem, and I am running my test code as Site Admin, is this a permissions isssue?

//Note: Here is a tricky part. 
//Since SharePoint prevents the creation of folder with name that corresponds to item id, we are going to:   
//1)create a folder with name in the following format '_<itemid>'
//2)rename a folder from '_<itemid>'' into '<itemid>'
//This allow to bypass the limitation of creating attachment folders
//^^WHY?

attachmentsFolder = attachmentsRootFolder.get_folders().add(itemId);//.add('_' + itemId);
//attachmentsFolder.moveTo(attachmentRootFolderUrl + '/' + itemId);
ctx.load(attachmentsFolder);

Code seemed to run fine once I ditched the .moveTo method and just passed the itemID to the add method and as per the code above, I didn't use the underscore. I went to the form afterwards and the attachments showed up, as well as if I browser to i.e. http://server/site/web/list/attachments/1/ it shows a web page instead of an error so it worked just fine without the .moveTo

Thanks. SG.

Error with the moveTo() method and original code:

Object doesn't support property or method 'moveTo'
0

The answer of @Vadim Gremyachev is nice and useful, but i would not check if the item had attachments, since admins could remove them from the db (using webdav) and the flag would be still on true. I would try to load the attachments folder, if it fails creted it, otherwise save atttachment:

var createFolder = function(itemId, folderUrl, callback){
    var ctx = SP.ClientContext.get_current();
    var web = ctx.get_web();
    var attachmentsRootFolder = web.getFolderByServerRelativeUrl(folderUrl);
    var attachmentsFolder = attachmentsRootFolder.get_folders().add('_'+itemId);
    attachmentsFolder.moveTo(folderUrl + itemId);
    ctx.load(attachmentsFolder);
    ctx.executeQueryAsync(function(){
        callback();

    }, function(request, response){
        console.log(response);
    });
}

var saveAttachment = function(itemId, filename, folderUrl, fileData){

    var ctx = SP.ClientContext.get_current();
    var web = ctx.get_web();
    var attachmentFolder = web.getFolderByServerRelativeUrl(folderUrl + itemId);
    var byteArray = new SP.Base64EncodedByteArray()

    for (var i = 0; i < fileData.length; ++i){
        byteArray.append(fileData[i]);
    }

    var createInfo = new SP.FileCreationInformation();
    createInfo.set_content(byteArray); 
    createInfo.set_url(filename);
    attachmentFolder.get_files().add(createInfo);

    ctx.executeQueryAsync(function(){

        //do what you want

    }, function(request, response){
        console.log(response);
    });
}

var filename = yourFileName;
var itemId = theRelatedItemId;
var attachmentsFolderUrl = 'Lists/MyList/Attachments/';

var ctx = SP.ClientContext.get_current();
var web = ctx.get_web();
var attachmentFolder = web.getFolderByServerRelativeUrl(attachmentsFolderUrl + itemId);
ctx.load(attachmentFolder);

ctx.executeQueryAsync(function(){

    saveAttachment(itemId, filename, attachmentsFolderUrl, array);

}, function(a,b){

    createFolder(itemId, attachmentsFolderUrl, function(){
        saveAttachment(itemId, filename, attachmentsFolderUrl, array);
    });
});

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.