SharePoint Online REST - Image upload via JavaScript/AJAX - javascript

I'm trying to upload an image to SharePoint using native JavaScript/jQuery - NOT SP.RequestExecutor.
I've cracked the authentication issue, nice and easy, so now it's just a case of working out how to upload binary files. If I put plain text in the file, it uploads fine, it's just binary data I'm having trouble with.
My code so far is included below. getToken() does it's thing and leaves me with a valid digest object to use. Also note I've blanked out the document library name with *'s.
function PerformUpload(fileName, fileData) {
getToken();
$.ajax({
url: siteFullUrl +
"/_api/web/GetFolderByServerRelativeUrl('/*****/')/Files" +
"/Add(url='" + fileName + "', overwrite=true)",
type: "POST",
async: false,
data: fileData,
processData: false,
contentType: "application/json;odata=verbose",
headers: {
"Accept": "application/json;odata=verbose",
"X-RequestDigest": digest
},
success: function (data) {
alert("Success");
},
error: function (err) {
alert("Error: \r\n" + JSON.stringify(err));
}
});
}
I've tried many combinations of different values for contentType, setting binaryStringRequestBody: true but the image is still corrupt when it comes into SharePoint.
My code at the moment to parse the file into binary is
var reader = new FileReader();
reader.onload = function (result) {
var fileName = '',
libraryName = '',
fileData = '';
var byteArray = new Uint8Array(result.target.result)
for (var i = 0; i < byteArray.byteLength; i++) {
fileData += String.fromCharCode(byteArray[i])
}
PerformUpload("image.jpg", fileData);
};
reader.readAsArrayBuffer(fileInput);
A file is being uploaded to SharePoint but if I try and view or download it it's corrupt.
Can anyone provide any guidance as to the correct way to upload a binary file to SharePoint? I should mention that if I replace (on the ajax call) data: fileData, with data: "A simple string", the file uploads and when I download it the contents of the file are A simple string.

If you are using SP.RequestExecutor to upload the file to SharePoint, you must be converted the ArrayBuffer into a string which can then be set as the body of a POST operation. See details here which guide you how to Upload file to SharePoint using REST by SP.RequestExecutor.
If you are using parsed file into binary with Jquery.Ajax, the image will corrupt when it comes into SharePoint. Also noted that the FileReader object accepts the file information for loading asynchronously. The onload and onerror events fire when the file is loaded successfully or fails. We should keep the proccess of onload event by default and get the result in onloadend event.
I tried the following articles and it work:
How to: Upload a file by using the REST API and jQuery
For simple, here is how I implemented:
var fileInput = jQuery('#getFile');
var file = fileInput[0].files[0];
var serverRelativeUrlToFolder = '*****'; //if the library in subsite, You have to remove the forward slash "/" before the document library relative url.
proccessUploadUsingJQueryAjax(file, serverRelativeUrlToFolder);
function getFileBuffer(file) {
var deferred = jQuery.Deferred();
var reader = new FileReader();
reader.onloadend = function (e) {
deferred.resolve(e.target.result);
}
reader.onerror = function (e) {
deferred.reject(e.target.error);
}
reader.readAsArrayBuffer(file);
return deferred.promise();
}
function addFileToFolderUsingJQueryAjax(fileName, arrayBuffer, serverRelativeUrlToFolder) {
// Construct the endpoint.
var fileCollectionEndpoint = String.format(
"{0}/_api/web/GetFolderByServerRelativeUrl('{1}')/files/add(overwrite=true, url='{2}')",
_spPageContextInfo.webAbsoluteUrl, serverRelativeUrlToFolder, fileName);
// Send the request and return the response.
// This call returns the SharePoint file.
return jQuery.ajax({
url: fileCollectionEndpoint,
type: "POST",
data: arrayBuffer,
processData: false,
contentType: "application/json;odata=verbose",
headers: {
"accept": "application/json;odata=verbose",
"X-RequestDigest": jQuery("#__REQUESTDIGEST").val()
}
});
}
function proccessUploadUsingJQueryAjax(file, serverRelativeUrlToFolder){
var getFile = getFileBuffer(file);
getFile.done(function (arrayBuffer) {
// Add the file to the SharePoint folder.
var addFile = addFileToFolderUsingJQueryAjax("image.jpg", arrayBuffer, serverRelativeUrlToFolder);
addFile.done(function (file, status, xhr) {
alert("File Uploaded");
});
addFile.fail(function (error) { alert("Error Add File: " + error.responseText); });
});
getFile.fail(function (error) { alert("Error Get File: " + error.responseText); });
}
Please let me know if it solved your problem.

Try adding this to your ajax settings
transformRequest: []
this will prevent Sharepoint from adding metadata to your file

Related

VichUploader and CroppieJS : how to send a base64 cropped image to persist in Symfony 4

I have a small symfony 4 application with a cropper using CroppieJS.
When i crop and hit the save button, croppie sends me a base64 image :
$( "#cropSave" ).click(function() {
basic.croppie('result','canvas'
).then(function (result) {}
how to send this result to my controller and persist the image with VichUploader and Doctrine ?
Here is my controller :
public function updateProfilePicture(Request $request): Response
{
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$user = $this->getUser();
$entityManager = $this->getDoctrine()->getManager();
$user->setImageFile($request->files->get('image'));
$entityManager->flush();
return new Response("ok");
}
I tried a lot of things but I must lack experience because it don't work :
var form = document.getElementById("myAwesomeForm");
var ImageURL = result;
// Split the base64 string in data and contentType
var block = ImageURL.split(";");
// Get the content type of the image
var contentType = block[0].split(":")[1];
// get the real base64 content of the file
var realData = block[1].split(",")[1];
// Convert it to a blob to upload
var blob = b64toBlob(realData, contentType);
// Create a FormData and append the file with "image" as parameter name
var formDataToUpload = new FormData(form);
formDataToUpload.append("image", blob);
or
function urltoFile(url, filename, mimeType){
return (fetch(url)
.then(function(res){return res.arrayBuffer();})
.then(function(buf){return new File([buf], filename, {type:mimeType});})
);
}
here is one of my ajax request :
$.ajax({
type : "POST",
data: formDataToUpload,
url : $('#updateProfilePictureLink').val(),
contentType:false,
processData:false,
cache:false,
dataType:"json",
success : function(response) {
$('#profilePicture').attr('src', result);
alert(response);
},
error : function (response) {
alert("error !");
}
});
I was thinking maybe "Simulate" a file upload in JS from the base64 using VichUploader formType input field, but I want to know if there are simpler ways.
Thanks
I managed to work around it thanks to Ronnie Hint.
You have to :
use JS FormData
put the blob inside
retrieve it in Symfony controller as an image
save it as is
But you have to implement serializable on your image's entity (serialize and unserialize all fields, unless it will break your other features).
Here is the working code sample :
// JS
$( "#cropSave" ).click(function() {
alert("click !");
basic.croppie('result','blob'
).then(function (result) {
var fd = new FormData();
//Third parameter is the blob name
fd.append('data',
result,$('#userId').val()+"."+result.type.split("/")[1]);
$.ajax({
type: 'POST',
url : $('#updateProfilePictureLink').val(),
data: fd,
processData: false,
contentType: false
}).done(function(data) {
// your things
});
// PHP
// Controller
try {
$this->denyAccessUnlessGranted('IS_AUTHENTICATED_FULLY');
$user = $this->getUser();
$entityManager = $this->getDoctrine()->getManager();
$user->setImageFile($request->files->get('data'));
$entityManager->flush();
}
catch (exception $e) {
}
// Entity
class User implements UserInterface, \Serializable
{
/** #see \Serializable::serialize() */
public function serialize()
{
return serialize(array(
$this->id,
$this->profilePicture,
$this->email,
$this->password
));
}
/** #see \Serializable::unserialize() */
public function unserialize($serialized)
{
list (
$this->id,
$this->profilePicture,
$this->email,
$this->password
) = unserialize($serialized);
}

Return PDF from Controller called via jQuery AJAX

I found some answers about using a controller to create a PDF from EvoPDF however none seem to deal with the controller being called via jQuery AJAX.
I have a simple jQuery function that sends data to a controller much like many others in my app:
$.ajax({
url: "/AnnualFees/showStatement",
cache: false,
data: {
authKey: memberData.authKey,
entityId: memberData.entityId,
barNumber: memberData.barNumber,
statementHTML: encodeURIComponent($("#statementBody").html())
},
method: "POST",
success: function (data) {
},
});
I followed all the samples and have this code. I can change it to save the PDF and confirm that the PDF is being generated.
public ActionResult getStatementPDF(string statementHTML)
{
//initialize the PdfConvert object
PdfConverter pdfConverter = new PdfConverter();
// set the license key - required
pdfConverter.LicenseKey = "uzUmNCcnNCYsIjQgOiQ0JyU6JSY6LS0tLQ==";
StringBuilder PDFBody = new StringBuilder();
PDFBody.Append("<!DOCTYPE html>");
PDFBody.Append("<html lang=\"en\">");
PDFBody.Append("<head>");
PDFBody.Append(" <meta charset=\"utf - 8\">");
PDFBody.Append(" <title>Statement</title>");
PDFBody.Append("</head>");
PDFBody.Append("");
PDFBody.Append("<body>");
PDFBody.Append("Hello world.");
PDFBody.Append("</body>");
PDFBody.Append("</html>");
byte[] outPdfBuffer = pdfConverter.GetPdfBytesFromHtmlString(PDFBody.ToString());
// Send the PDF file to browser
FileResult fileResult = new FileContentResult(outPdfBuffer, "application/pdf");
fileResult.FileDownloadName = "Statement.pdf";
return fileResult;
}
I can confirm their are no errors and that a 200 success is returned with the right application/pdf type and about the same size as on disk. However, no PDF ever appears, nothing opens in the browser.
You need to handle the data onSuccess in the ajax call, you can do something like this to open the file, you may want to use FileSaverJS (https://github.com/eligrey/FileSaver.js/) if you want to save the file
success: function (data) {
var file = new Blob([data], { type: 'application/pdf' });
var fileURL = URL.createObjectURL(file);
window.open(fileURL);
}

Javascript download document in browser. Web API

I'm using a Web API to fetch my document using the following:
[Route("Api/DocumentApi/DownloadDocument")]
[HttpGet]
public IHttpActionResult DownloadDocument(int documentID)
{
Document documentToDownload = new Document();
using (TrustInvestmentSwitchEntities db = new TrustInvestmentSwitchEntities())
{
DocumentRepository repo = new DocumentRepository();
documentToDownload = repo.GetSingle(db, x => x.ID == documentID);
}
var stream = new MemoryStream();
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(stream.GetBuffer())
};
result.Content.Headers.ContentDisposition = new System.Net.Http.Headers.ContentDispositionHeaderValue("attachment")
{
FileName = documentToDownload.FileName
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");
var response = ResponseMessage(result);
return response;
}
This looks like its working at retrieves the document. However, I want the document to either download immediately or show a popup for the user to select a location to save the file and this is not happening. Download immediately is preferred.
Here is my Javascript GET which I think is the problem:
DocumentToDownload = $(that).closest('.document-item').data('documentid');
var url = '/Api/DocumentApi/DownloadDocument';
var data = {
DocumentID: DocumentToDownload
};
$.ajax({
type: "GET",
url: url,
contentType: "application/json",
data: data,
dataType: "json",
success: function (json, status) {
if (status != "success") {
log("Error loading data");
return;
}
log("Data loaded!");
},
error: function (result, status, err) {
log("Error loading data");
return;
}
});
Im unsure what to put after:
success: function (json, status) {
Ajax file downloads are not allowed for security reasons (otherwise any site could download any file to the users machine in the background)
No need to use an ajax call, you can trigger the download without reloading the page using a normal link if the href is pointing to a URL that returns a document (the header is a document) which it looks like your API is doing. So you could simply do:
Download
Where the DocumentID is set to the ID of the document you want to download. When the user clicks the link the page won't change/refresh

File upload through AJAX call, it's possible or not? I'm confused

I'm working on an application where I'm sending data from a form by serializing it and then sending it through AJAX. Everything works perfect but I've got the first headache is that when the form has a field of type FILE for upload any kind of file (should be restricted to images only but this is another topic) to my server-side (Symfony2 controller action) code does not get the image field and therefore can not perform any action on the same.
I've done some research (1,2,3,4 and many more docs found at Google) and in some cases it is said that it's possible and in some instance says it is not, others use the XHR object and then I'm confused and not know where to go from here, here I leave my code to throw him a look and tell me what's wrong and what should I do to make the upload if possible (I think it's):
$.ajax({
async: false,
type: "POST",
cache: false,
url: '{{ path('update-product', {'id': id}) }}',
data: $('.smart-form').serializeArray(),
success: function (data) {
// here goes a message if all was fine
}
});
This is the HTML field:
<input type="file" id="product_imageFile_file" name="product[imageFile][file]" class="valid">
Of course my form has enctype="multipart/form-data" and this is the request I got from Symfony2 controller action:
array(4)
[product_name]: string (10) "Producto-1"
[active]: string (1) "1"
[product_description]: string (21) "DescripcionProducto-1"
[_token]: string (43) "sjcpVT34_9-V7Z2doTZsvZsAewO-0Q5hD-a9C6VPNc4"
And as can see here there is not product[imageFile][file] on the request so something is wrong in my jQuery code, can any give me some help or point me to the right place?
NOTE: Since I'm using PHP and some of it's bundles to handle file upload and then manage the entities I don't know if will be a good practice to use any of the thousands of jQuery plugins to do that, that's the cause I don't mention here and I don't use them at all
Thanks in advance
Well, finally and after a lot of research I get it and without any external library as some users suggest here, maybe it's a ugly method but it works for me and does what I want. Here is the code:
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process image files.
if (!f.type.match('image.*')) {
// Display some alert or show the user the file is wrong
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function (theFile) {
return function (e) {
// Render thumbnail.
var span = document.createElement('span');
span.innerHTML = ['<img id="product_image" class="thumb" src="', e.target.result, '" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('pImage').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('imageFile_file').addEventListener('change', handleFileSelect, false);
$.ajax({
async: false,
url: url, // the URL for process the whole form
type: 'POST',
data: $('#formID').serialize() + '&img=' + JSON.stringify($("#product_image").attr('src')),
cache: false,
success: function (data)
{
if (typeof data.error === 'undefined')
{
// Handle success here
}
else
{
// Handle errors here
}
},
error: function (jqXHR, textStatus, errorThrown)
{
// Handle errors here
},
complete: function ()
{
// STOP LOADING SPINNER
}
});
Hope helps someone else

How can I send the contents of a file to my server?

I'm trying to let users import an OPML file that I parse server (rails app) side. I'm having trouble as it seems that my server isn't getting the info (neither the success nor error functions run and even if I hardcode other data into the call, the call doesn't change).
Here's what I have embedded into the page:
<script>
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
// Loop through the FileList
for (var i = 0, f; f = files[i]; i++) {
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Print the contents of the file
var span = document.createElement('span');
span.innerHTML = ['<p>',e.target.result,'</p>'].join('');
document.getElementById('list').insertBefore(span, null);
};
$.ajax({
type: 'GET',
url: "/parse_opml",
data: {file: f},
success: function(details, response) {
console.log('woo!');
},
error: function(data, response) {
console.log('boooo');
}
});
})(f);
// Read in the file
reader.readAsText(f);
}
}
document.getElementById('the_o').addEventListener('change', handleFileSelect, false);
</script>
<input id="the_o" name="files[]" type="file">
Looking at chrome's network panel, I'm seeing the call: Request URL:blob:http%3A//localhost%3A3000/14e2be6b-059f-47f5-ba37-97eda06242b4 whose preview and response is the content of my .txt file. But like I said, the server never gets that text, so I'm puzzled.
Any help is greatly appreciated, thanks!
ANSWER
I ended up just using this: JavaScript: Upload file
Client code:
%form{:enctype => 'multipart/form-data', :action => '/parse_opml', :method => 'post'}
%input{:type => 'file', :name => 'file', :id => 'the_o'}
%input{:type => 'submit', :value => 'go'}
Server code:
f = File.open(params[:file].tempfile, 'r')
c = f.read
Works like a charm!
Javascript can't post uploaded files to the server as it is a limitation (for security reasons I assume).
Take a look at this other question regarding posting files posted through javascript:
JavaScript: Upload file
The answer on that questions says you can only do it using flash, but there are also iframe alternatives for upload and post.
Take a look at this as well for an alternative solution:
https://github.com/Widen/fine-uploader
Your ajax request isn't event sent as you return from your onload function before it.
You can send files via ajax on up to date browsers using XHR2
reader.onload = (function(theFile) {
var data = new FormData();
data.append('file',theFile);
$.ajax({
type: 'POST',
processData: false,
contentType: false,
url: "/parse_opml",
data: data,
success: function(details, response) {
console.log('woo!');
},
error: function(data, response) {
console.log('boooo');
}
});
return function(e) {
// Print the contents of the file
var span = document.createElement('span');
span.innerHTML = ['<p>',e.target.result,'</p>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);

Categories

Resources