Question

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?

Was it helpful?

Solution

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>

OTHER TIPS

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);
};

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;
}

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);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with sharepoint.stackexchange
scroll top