Take the 2-minute tour ×
SharePoint Stack Exchange is a question and answer site for SharePoint enthusiasts. It's 100% free, no registration required.

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?

share|improve this question
    
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 '13 at 22:40

3 Answers 3

up vote 5 down vote accepted

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>
share|improve this answer
    
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! –  Anatoly Mironov Feb 26 '13 at 15:09
    
This should be marked as the answer - it works perfectly in my test environment. –  Andrey Feb 27 '13 at 4:08
    
This method gives error "Conflict" if i upload file size of 1mb. Is there any way to make this synchronous? –  Unnie Mar 7 at 12:05

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);
};
share|improve this answer

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;
}
share|improve this answer
    
Thanks Danny, but unfortunatelly I could not make it work with reader.readAsDataURL(file); / var contents = convertDataURIToBinary(event.target.result); –  Fedor Shihantsov Feb 26 '13 at 12:15

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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