13

I am using the following REST code to add a file as an attachment to a SharePoint 2013 list item.

<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

<SharePoint:ScriptLink language="javascript" name="~site/Scripts/js/jquery-1.8.2.js" OnDemand="false" runat="server" Localizable="false"/>

<input id="ufile" type="file" ></input>

<script type="text/javascript">
$(document).ready( 
    doFunc() 
);

function doFunc() {
    var control = document.getElementById("ufile");
    control.addEventListener("change", doAttach, false);
}

var fileName;

function doAttach(event) {
    var files = event.srcElement.files;

    if (!window.FileReader) {
        alert("The FileSystem APIs are not fully supported in this browser.");
        return false;
    }           

    if (files.length > 0) {
        var file = files[0];
        fileName = file.name;

        var reader = new FileReader();
        reader.onload = onLoad;

        reader.onerror = function(event) {
            console.error("File reading error " + event.target.error.code);
        };

        reader.readAsDataURL(file);

    }//     
    return false;
}

function onLoad(event) {
    var contents = event.target.result;

    $.ajax({
        url: "/Test1/_api/web/lists/GetByTitle('List1')/items(1)/AttachmentFiles/add(FileName='" + fileName + "')",
        type: "POST",
        contentType: "application/octet-stream;odata=verbose",
        data: contents, 
        headers: {
                    "X-RequestDigest": $("#__REQUESTDIGEST").val()
                 },

        success: function (data)
        {
            alert("success");
        },

        error: function (data)
        {
            alert('error' + data.status + ':' + data.statusText + '\n' + data.responseText);
        }
    });
}
</script>       
</asp:Content>

The file is uploaded, but contains "incorrect" data.

For example a jpeg file begins as follows: data:image/jpeg;base64,/9j/4AAQSkZJRgABAQEAYABgAAD/...

Does anybody know whether the SharePoint 2013 REST API allows uploading non-text files correctly?

1
  • I'll add that this is definitely possible, as I've done it with the REST API from within Objective C in an iPhone app. Your code almost looks right but I don't think you're putting the correct content payload into the request.
    – James Love
    Feb 25, 2013 at 22:40

4 Answers 4

9

I was able to add a file with SP.RequestExecutor

<%-- _lcid="1033" _version="15.0.4128" _dal="1" --%>
<%-- _LocalBinding --%>
<%@ Page language="C#" MasterPageFile="~masterurl/default.master"    Inherits="Microsoft.SharePoint.WebPartPages.WebPartPage,Microsoft.SharePoint,Version=15.0.0.0,Culture=neutral,PublicKeyToken=71e9bce111e9429c" meta:progid="SharePoint.WebPartPage.Document" meta:webpartpageexpansion="full"  %>
<%@ Register Tagprefix="SharePoint" Namespace="Microsoft.SharePoint.WebControls" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="Utilities" Namespace="Microsoft.SharePoint.Utilities" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Import Namespace="Microsoft.SharePoint" %> <%@ Assembly Name="Microsoft.Web.CommandUI, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %> <%@ Register Tagprefix="WebPartPages" Namespace="Microsoft.SharePoint.WebPartPages" Assembly="Microsoft.SharePoint, Version=15.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
<asp:Content ContentPlaceHolderId="PlaceHolderMain" runat="server">

<SharePoint:ScriptLink language="javascript" name="~site/Scripts/js/jquery-1.8.2.js" OnDemand="false" runat="server" Localizable="false"/>

<input id="ufile" type="file" ></input>

<script type="text/javascript">
$(document).ready( dofunc() );

function dofunc() {
    var control = document.getElementById("ufile");
    control.addEventListener("change", fdocattach, false);
}

var file;
var contents;

function fdocattach(event) {
    var i = 0,
    files = event.srcElement.files,
    len = files.length;

    for (; i < len; i++) {
        console.log("Filename: " + files[i].name);
        console.log("Type: " + files[i].type);
        console.log("Size: " + files[i].size + " bytes");
    }

    if (!window.FileReader) {
        alert("The FileSystem APIs are not fully supported in this browser.");
        return false;
    }           

    if (files.length > 0) {
        file = files[0];
        fileName = file.name;

        var reader = new FileReader();
        reader.onload = fonload;

        reader.onerror = function(event) {
            console.error("File reading error " + event.target.error.code);
        };
        reader.readAsArrayBuffer(file);
    }       
    return false;
}

function _arrayBufferToBase64(buffer) {
    var binary = ''
    var bytes = new Uint8Array(buffer)
    var len = bytes.byteLength;
    for (var i = 0; i < len; i++) {
        binary += String.fromCharCode(bytes[i])
    }
    return binary;

}

function fonload(event) {
    contents = event.target.result;
            $.getScript("https://addinexpress966.sharepoint.com/Test1/_layouts/15/SP.RequestExecutor.js", fonload2);
}

function fonload2() {
    var contents2 = _arrayBufferToBase64(contents);

    var createitem = new SP.RequestExecutor("https://addinexpress966.sharepoint.com/Test1");
    createitem.executeAsync({
        url: "https://addinexpress966.sharepoint.com/Test1/_api/web/lists/GetByTitle('List1')/items(1)/AttachmentFiles/add(FileName='" + file.name + "')",
        method: "POST",
        binaryStringRequestBody: true,
        body: contents2,
        success:  fsucc,
        error: ferr,
        state: "Update"
    });

    function fsucc(data)
    {
        alert('success');
    }       
    function ferr(data)
    {
        alert('error\n\n' + data.statusText + "\n\n" + data.responseText);
    }       
}

</script>       

</asp:Content>
4
  • Good to know. It is really an interesting question. I tried to find a solution. What I found was that jQuery somehow parsed or encoded the values. Thanks for sharing! Feb 26, 2013 at 15:09
  • This should be marked as the answer - it works perfectly in my test environment.
    – Andrey
    Feb 27, 2013 at 4:08
  • 1
    This method gives error "Conflict" if i upload file size of 1mb. Is there any way to make this synchronous?
    – Unnie
    Mar 7, 2014 at 12:05
  • How would this work when reading from a file that is currently hosted on SP? A simple "GET" seems to not return the data in a readable format.
    – Dinerdo
    Sep 10, 2015 at 21:09
4

After reading up and a lot of fiddling i came up with setting the data attribute to the file and processData to false. This worked great, except for dropfile not supporting readAsArrayBuffer.

So i used the above convertDataURIToBinary function. Now i have this and it works great. Remarks: files.data is added on the load event and i had to turn async of when attaching multiple files to a listitem.

jQuery.ajax({
    url: listUrl + "/items(" + id + ")/AttachmentFiles/add(FileName='" + file.name + "')",
    type: "POST",
    async: false,
    processData: false,
    headers: {
        "accept": "application/json;odata=verbose",
        "X-RequestDigest": jQuery("#__REQUESTDIGEST").val()
    },
    data: convertDataURIToBinary(file.data),
    success: function(e) { _.log(_.logType.verbose, "File uploaded: " + e); },
    error: function(e) { _.log(_.logType.warning, "File not uploaded: " + e); }
});

var convertDataURIToBinary = function(dataURI) {
    var base64Marker = ";base64,";
    var base64Index = dataURI.indexOf(base64Marker) + base64Marker.length;
    var base64 = dataURI.substring(base64Index);
    var raw = window.atob(base64);
    var rawLength = raw.length;
    var array = new window.Uint8Array(new window.ArrayBuffer(rawLength));

    for (i = 0; i < rawLength; i++) {
        array[i] = raw.charCodeAt(i);
    }
    return array;
};

var filesDropped = function (e) {
    e = e || window.event;
    var files = (e.files || e.dataTransfer.files);

    jQuery(files).each(function (index, element) {
        var fileReader = new FileReader();
        fileReader.file = element;
        fileReader.onload = fileLoaded;
        fileReader.onerror = function () {
            _.log(_.logType.warning, "Error reading file: " + this.file.name);
        };
        fileReader.readAsDataURL(element);
    });

    return false;
};

var fileLoaded = function (fileOnloadEvent) {
    this.file.data = fileOnloadEvent.target.result;
    var filename = this.file.name.toLowerCase();
    jQuery(vm.files()).each(function (fIndex, fElement) {
        if (fElement.name.toLowerCase() === filename)
            vm.removeFile(fElement);
    });
    vm.files.push(this.file);
};
0

In this blog post, I use a very similar technique to upload a file from a SharePoint 2013 app using JSOM. I believe the key to what you are looking to do is the utility method convertDataURIToBinary, which will strip out the Base64 prefix and store the data in a binary byte array:

// Utility function to remove base64 URL prefix and store base64-encoded string in a Uint8Array
// Courtesy: https://gist.github.com/borismus/1032746
function convertDataURIToBinary(dataURI)
{
    var BASE64_MARKER = ';base64,';
    var base64Index = dataURI.indexOf(BASE64_MARKER) + BASE64_MARKER.length;
    var base64 = dataURI.substring(base64Index);
    var raw = window.atob(base64);
    var rawLength = raw.length;
    var array = new Uint8Array(new ArrayBuffer(rawLength));

    for (i = 0; i < rawLength; i++)
    {
        array[i] = raw.charCodeAt(i);
    }
    return array;
}
1
  • Thanks Danny, but unfortunatelly I could not make it work with reader.readAsDataURL(file); / var contents = convertDataURIToBinary(event.target.result); Feb 26, 2013 at 12:15
0

With out file reader.Like safari 5.0. call openpageindialog on link.

function openpageindialog(itemid) {

    var url = "/_layouts/Attachfile.aspx?ListId={LISTID}&ItemId=" + ItemID;
    var options = {
        url: url,
        width: 600,
        height: 400,
        dialogReturnValueCallback: function(result, fileAdded){

            if(result == SP.UI.DialogResult.OK){
                //update list of files
                console.log("done");
            }
        }
    };
    SP.UI.ModalDialog.showModalDialog(options);
}

Your Answer

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

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