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

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

Related

Rails form submission by AJAX with an active storage attachment

I have a form that I want to submit using AJAX. The form lets you upload a picture as an attachment, among other fields. Now using pure rails it works just fine, and the AJAX post function I have set up also works...until I try to upload this image file. It just submits without the file as if I did not attach it. What is the correct flow for this? the ajax function
function postInstrument() {
$("form#new_instrument").submit(function(e) {
e.preventDefault();
$.ajax({
type: "POST",
url: `http://localhost:3000/users/${userId}/instruments`,
data: $(this).serialize(),
dataType: "json",
success: document.getElementById("new-instrument-form-div").innerHTML = 'Instrument Added!'
})
})
}
The solution was just to wrap my form in a FormData object.
You are using .serialize for your image data. And that is not possible.
This link is by far the best for you to read about file uploading https://developer.mozilla.org/en-US/docs/Web/API/File/Using_files_from_web_applications
function FileUpload(img, file) {
const reader = new FileReader();
this.ctrl = createThrobber(img);
const xhr = new XMLHttpRequest();
this.xhr = xhr;
const self = this;
this.xhr.upload.addEventListener("progress", function(e) {
if (e.lengthComputable) {
const percentage = Math.round((e.loaded * 100) / e.total);
self.ctrl.update(percentage);
}
}, false);
xhr.upload.addEventListener("load", function(e){
self.ctrl.update(100);
const canvas = self.ctrl.ctx.canvas;
canvas.parentNode.removeChild(canvas);
}, false);
xhr.open("POST", "http://demos.hacks.mozilla.org/paul/demos/resources/webservices/devnull.php");
xhr.overrideMimeType('text/plain; charset=x-user-defined-binary');
reader.onload = function(evt) {
xhr.send(evt.target.result);
};
reader.readAsBinaryString(file);
}
And here How can I upload files asynchronously?

Is there a way to upload file before custom button click,and then using ajax send it to controller action method?

I am quite new to Telerik Html Kendo. My aim is to upload a file first. Then, invoke the corresponding action method in 'Administration' controller through ajax that should take the uploaded file and other parameters on clicking the 'Submit' button as shown in image below.
Most of Telerik examples show asynchronous upload feature which invokes controller method to upload file. I don't want to do this way.
However, I have tried to upload file with onSelect event of kendo upload. It shows that file gets included however it doesn't upload.
As a result, I am unable to see any info. about uploaded file in onSuccess and onComplete event.
I used formData on 'Submit' button click. However I receive null at the action method all time.
Any correct way of doing this?
Here is my html code for file upload:
<div class="well well-sm" style="width:inherit;text-align: center;float:left;">
<!--form method="post"-->
<!--div class="demo-section k-content">
<input name="files" id="files" type="file" value="Upload a Data File"/>
</div-->
<!--/form-->
#(Html.Kendo().Upload()
.Name("files")
.Multiple(false)
.Messages(m => m.Select("Please upload a Data File"))
.HtmlAttributes(new { aria_label = "file" })
.Events(events => events
.Complete("onComplete")
.Select("onSelect")
.Success("onSuccess")
.Upload("onUpload")
.Progress("onProgress"))
//.Async(a=>a.AutoUpload(false))
.Validation(v => v.AllowedExtensions(new string[] { ".csv", ".xls", ".xlsx", ".txt" }))
)
</div>
Here is the javascript code for all the js events I want to invoke.
<script>
var _files;
function onSelect(e) {
var files = e.files;
alert(files[0].name);
_files = files[0];
//file = files[0].name;
var acceptedFiles = [".xlsx", ".xls", ".txt", ".csv"]
var isAcceptedFormat = ($.inArray(files[0].extension, acceptedFiles)) != -1
if (!isAcceptedFormat) {
e.preventDefault();
$("#meter_addt_details").addClass("k-state-disabled");
//$("#submit_btn").addClass("k-state-disabled");
document.getElementById("submit_btn").disabled = true;
alert("Please upload correct file. Valid extensions are xls, xlsx,txt,csv");
}
else {
/* Here I tried to upload file didn't work out */
$("#meter_addt_details").removeClass("k-state-disabled");
// $("#submit_btn").removeClass("k-state-disabled");
document.getElementById("submit_btn").disabled = false;
#*
$("#files").kendoUpload({
async: {
saveUrl: '#Url.Action("ReadMeterFile","Administration")',
autoUpload: false
}
}); *#
$("#files").kendoUpload();
//$(".k-upload-selected").click();
#*var upload = $("#files").data("kendoUpload");
upload.upload(); *#
}
}
#*
function onUpload(e) {
$(".k-upload-selected").trigger('click');
//console.log("Upload :: " + getFileInfo(e));
}
function onSuccess(e) {
console.log(files[0].name);
_files = e.files[0];
}
function onProgress(e) {
console.log("Upload progress :: " + e.percentComplete);
}
function onComplete(e) {
console.log("Complete");
}
function onSubmitButtonClick(e) {
var formData = new FormData();
alert(_files.name);
formData.append('files', _files);
formData.append('order_num', $("#order").val());
formData.append('purchase_order', $("#purchase_order").val());
$.ajax({
url: '#Url.Action("ReadFile","Administration")',
data: formData,
type: 'POST',
processData: false,
contentType: false,
dataType: "json",
success: function (data) {
alert("Good");
}
});
}
</script>
Here is my controller:
public ActionResult ReadFile(IEnumerable<HttpPostedFileBase> files,string order_num, string purchase_order)
{
System.Diagnostics.Debug.WriteLine("File length:"+files.ToList().Capacity);
foreach(var f in files)
{
System.Diagnostics.Debug.WriteLine(f.FileName);
var fileName = Path.GetFileName(f.FileName);
var destinationPath = Path.Combine(Server.MapPath("~/App_Data"), fileName);
f.SaveAs(destinationPath);
}
//System.Diagnostics.Debug.WriteLine(file);
/*
System.Diagnostics.Debug.WriteLine("File:"+files);
System.Diagnostics.Debug.WriteLine("Order:"+order_num);
System.Diagnostics.Debug.WriteLine("Purchase Order:"+purchase_order);
return Content("");
}
Here is some code I used before for manually uploading from the kendo upload widget. From your question I think what you are looking for is the way to correctly get the info on the controller side. However, I will add the code I have used which should help you out. (My code uploads a PDF)
#(Html.Kendo().Upload()
.Name("pdf-file")
.Multiple(false)
.Validation(v => v.AllowedExtensions(new string[] { "pdf" }))
.Events(ev => ev.Select("pdfSelected"))
)
function pdfSelected(e) {
if (e.files != null && e.files.length > 0 && e.files[0] != null) {
var file = e.files[0];
if (file.validationErrors != null && file.validationErrors.length > 0) {
return; //These errors will show in the UI
}
var formData = new FormData();
formData.append('PdfFile', file.rawFile);
formData.append('AdditionalValue', 'Some String');
$.ajax({
type: 'post',
url: '[SOMEURL]',
data: formData,
dataType: 'json',
contentType: false,
processData: false,
success: pdfUploaded
});
}
}
function pdfUploaded(data) {
//Whatever you want.
}
Inside of pdfSelected is the code that actually posts the file. If you want to do it all at the same time along with other properties, via a submit button. Then instead of performing the upload there. Do nothing but your validation for the upload or don't implement pdfSelected and wait until submit is clicked to perform the validation(probably better). Then on your button click
//Or course check files.length to avoid errors. Not added to keep it short
var files = $('#pdf-file').data('kendoUpload').getFiles();
var file = files[0];
Everything from "var formData = new FormData();" down, from the code above remains the same. Here is the controller code.
public ActionResult MyAction() {
string additionalValue = (string) this.Request.Form["AdditionalValue"];
HttpPostedFileBase file = this.Request.Files["PdfFile"];
//Do whatever you need to with them.
}
The file's rawFile property is what you need not just the file object because that is kendo specific.

Uploading File via FormData

I am trying to upload a file to a Node backend that uses Multer. Multer requires the form to be submitted a specific way. If it's not submitted that way, the request.file parameter will be undefined. I have created an approach that works by using brute force. That working approach looks like this:
index-1.html:
<form method="POST" enctype="multipart/form-data" id="fileUploadForm">
<input type="file" id="selectedFile" name="selectedFile" /><br/><br/>
<input type="submit" value="Submit" id="btnSubmit"/>
</form>
...
var btn = document.getElementById('btnSubmit');
btn.addEventListener('click', function(e) {
e.preventDefault();
var form = $('#fileUploadForm')[0];
var data = new FormData(form);
console.log(data);
$.ajax({
type: "POST",
enctype: 'multipart/form-data',
url: "/upload",
data: data,
processData: false,
contentType: false,
cache: false,
timeout: 600000,
success: function (data) {
console.log("SUCCESS!");
},
error: function (e) {
console.log("ERROR : ", e);
}
});
});
The code above successfully posts the file to my server. That server has the following:
server.js
app.post('/upload', upload.single('selectedFile'), function(req, res) {
if (req.file) {
console.log('file uploaded');
} else {
console.log('no file');
}
res.send({});
});
Using the code above, "file uploaded" is displayed in the console window as expected. However, I need a more dynamic approach. For that reason, I need to programmatically build the form in JavaScript. In an attempt to do that, I've created the following:
index-2.html
[my UI]
var btn = document.getElementById('btnSubmit');
btn.addEventListener('click', function(e) {
var form = document.createElement('form');
form.action = '/upload';
form.method = 'POST';
form.enctype = 'multipart/form-data';
var node = document.createElement("input");
node.name = 'selectedFile';
node.value = GLOBAL_SELECTED_FILE;
form.appendChild(node);
var data = new FormData(form);
data.append('id', this.id);
console.log(data);
$.ajax({
type: 'POST',
url: '/profile-picture/upload',
enctype: 'multipart/form-data',
data: data,
contentType: false,
processData: false,
success: function(res) {
console.log('success!');
},
error: function(xhr, status, err) {
console.log('error');
}
});
});
This second approach does not work. To clarify, the GLOBAL_SELECTED_FILE variable is the data of a file that was selected from an input element. The data is loaded via the FileReader api. That looks like this:
var GLOBAL_SELECTED_FILE = null;
var fileReader = new FileReader();
fileReader.onload = function(e) {
GLOBAL_SELECTED_FILE = e.target.result;
}
fileReader.readAsDataURL(fileSelected); // file selected comes from the onchange event on a <input type="file">..</input> element
Basically, I'm loading a preview of the image. Anyways, when I hit the submit button in the working version (index-1.html), I notice in Fiddler that a different value is sent over the value sent in index-2.html.
With the approach in index-1.html, Fiddler shows something like this in the "TextView" tab:
------WebKitFormBoundary183mBxXxf1HoE4Et
Content-Disposition: form-data; name="selectedFile"; filename="picture.PNG"
Content-Type: image/png
PNG
However, when I look in the "TextView" tab in Fiddler for the data sent via index-2.html, I see the following:
------WebKitFormBoundary9UHBP02of1OI5Zb6
Content-Disposition: form-data; name="selectedFile"
data:image/png;base64,iVBORw0KGg[A LOT MORE TO GO]
It's like the FormData is using two different encodings for the same value. Yet, I don't understand why. How do I get index-2.html to send the image in the same format as index-1.html so that Multer will populate the req.file property?
Thank you!
In index-1.html, you are using a file input:
<input type="file" id="selectedFile" name="selectedFile" />
In index-2.html, you are creating an ordinary form input (not a file input):
var node = document.createElement("input");
node.name = 'selectedFile';
node.value = GLOBAL_SELECTED_FILE;
form.appendChild(node);
To create a file input, you would need to add node.type = 'file'. However, you won't be able to set the value because browser security restrictions prevent setting the value of file inputs.
Instead, what you need to do is append the file that was selected by the user to the FormData object:
var data = new FormData(form);
data.append('id', this.id);
data.append('selectedFile', $('#fileInputElement')[0].files[0]);

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

SharePoint Online REST - Image upload via JavaScript/AJAX

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

Categories

Resources