I'm writing an app that uploads multiple posts to Tumblr. I'm using Ruby with Sinatra, and since I'm new to both I was wondering how to actually use the files uploaded using the HTML5 multiple file reader.
Here is my Ruby pseudocode in app.rb:
post '/' do |file|
if file is text file
##client.text("#{session[:user_id]}.tumblr.com", {:body => "Stuff from index.erb"})
elsif file is photo file
##client.photo("#{session[:user_id]}.tumblr.com", {:data => ['/path/to/pic.jpg']})
else
"Sorry, text and photo only at this time."
end
end
And here's my HTML/Javascript in index.erb:
<body>
<h1>File Uploader</h1>
<form method="post">
<input type="file" id="files" name="files[]" multiple="multiple">
<output id="list"></output>
<div id="progress_bar"><div class="percent">0%</div></div>
<input type="submit" value="Post Something!">
</form>
<!-- FILE LOADER -->
<script>
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.*')) {
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 class="thumb" src="', e.target.result,
'" title="', escape(theFile.name), '"/>'].join('');
document.getElementById('list').insertBefore(span, null);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
<!-- PROGRESS BAR -->
<script>
var reader;
var progress = document.querySelector('.percent');
function abortRead() {
reader.abort();
}
function errorHandler(evt) {
switch(evt.target.error.code) {
case evt.target.error.NOT_FOUND_ERR:
alert('File Not Found!');
break;
case evt.target.error.NOT_READABLE_ERR:
alert('File is not readable');
break;
case evt.target.error.ABORT_ERR:
break; // noop
default:
alert('An error occurred reading this file.');
};
}
function updateProgress(evt) {
// evt is an ProgressEvent.
if (evt.lengthComputable) {
var percentLoaded = Math.round((evt.loaded / evt.total) * 100);
// Increase the progress bar length.
if (percentLoaded < 100) {
progress.style.width = percentLoaded + '%';
progress.textContent = percentLoaded + '%';
}
}
}
function handleFileSelect(evt) {
// Reset progress indicator on new file selection.
progress.style.width = '0%';
progress.textContent = '0%';
reader = new FileReader();
reader.onerror = errorHandler;
reader.onprogress = updateProgress;
reader.onabort = function(e) {
alert('File read cancelled');
};
reader.onloadstart = function(e) {
document.getElementById('progress_bar').className = 'loading';
};
reader.onload = function(e) {
// Ensure that the progress bar displays 100% at the end.
progress.style.width = '100%';
progress.textContent = '100%';
setTimeout("document.getElementById('progress_bar').className='';", 2000);
}
// Read in the image file as a binary string.
reader.readAsBinaryString(evt.target.files[0]);
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
</body>
Once I have the actual file objects, I can send them to Tumblr via the Ruby code, BUT I don't really know how to get the file from index.erb to the Ruby code. Should I put the Ruby code inside the .erb file?
TL;DR:
1.) How can I convert whatever the Javascript returns into a readable file for Ruby? I want to return the photos as files, and the text as parseable text.
2.) How can I send the file returned by Javascript to Ruby to send to Tumblr?
Related
I have an HTML form that is used to upload a file to the server. This works correctly but now I am trying to expand the capability such that I select a tar file that consists of two binary files. Then untar the files and based on certain conditions either upload the first or the second file.
This is what I have done so far
use FileReader to read the tar file as ByteArray
use untar from js-untar to untar both file
I need help to figure out how to take the ByteArray for either files and add then to the FormData so that I can upload them.
Any help would be appreciated.
Here are snippets from my code
HTML Form
<form id="upform" enctype="multipart/form-data"
action="cgi-bin/upload2.cgi">
Firmware file: <input id='userfile' name="userfile" type="file" width=50 >
<input type
="submit" name="submitBtn" value="send file">
</form>
Untar code
function sendData() {
var formData = new FormData(form);
var action = form.getAttribute('action');
filesize = file.files[0].size;
var reader = new FileReader();
reader.onload = function() {
untar(reader.result).then(
function (extractedFiles) { // onSuccess
console.log('success');
formData.delete('userfile');
var reader2 = new FileReader();
reader2.onload = function() {
formData.append('userfile', reader2.result);
upload(formData);
}
var blob = new Blob([extractedFiles[0]], {type : 'multipart/form-data'});
reader2.readAsDataURL(blob);
// var f = URL.createObjectURL(blob);
},
function (err) {
console.log('Untar Error');
}
)
};
reader.readAsArrayBuffer(file.files[0]);
return;
}
function upload(formData) {
var action = form.getAttribute('action');
reqUpload.open('POST', action, true);
reqUpload.onreadystatechange = uploadState;
document.body.style.cursor = "wait";
var ld = document.getElementById("load");
ld.classList.add("loader");
reqUpload.send(formData);
document.getElementById('progress').style.display = "block";
progTimer = setInterval(ping, 10000);
uploadStarted = true;
return;
}
Having image preview option, that show the thumb of selected images.
Consider the below HTML,
html:
<input type="file" multiple accept="image/x-png,image/gif,image/jpeg" title="Upload Image" id="gallery_img" name="roomimage[]" />
Code used for previewing image in browser:
$(document).ready(function() {
if (window.File && window.FileList && window.FileReader) {
$("#gallery").on("change", function(e) {
var fileName = document.getElementById("gallery").value;
var idxDot = fileName.lastIndexOf(".") + 1;
var extFile = fileName.substr(idxDot, fileName.length).toLowerCase();
if (extFile=="jpg" || extFile=="jpeg" || extFile=="png"){
//TO DO
var files = e.target.files,
filesLength = files.length;
for (var i = 0; i < filesLength; i++) {
var f = files[i]
var fileReader = new FileReader();
fileReader.onload = (function(e) {
var file = e.target;
$("<span class=\"imgPreview\">" +
"<img id=\"image\" name=\"image[]\" class=\"imageThumb\" src=\"" + e.target.result + "\"/>" +
"<span class=\"remove icon-error\" title=\"Close\"></span>" +
"</span>").insertAfter(".gallery_section");
$(".remove").click(function(){
$(this).parent(".imgPreview").remove();
});
});
fileReader.readAsDataURL(f);
}
}else{
alert("Only jpg/jpeg and png files are allowed!");
}
});
} else {
alert("Your browser doesn't support to File API")
}
});
While choosing files with the above html all works fine. like i can select multiple images and i can upload that multiple images via ajax.
ajax:
var formData = new FormData($("#room_form")[0]);
var url=$("#room_form").attr("action");
$.ajax({
method: "POST",
data: formData,
url: url,
cache: false,
contentType: false,
processData: false,
})
.success(function(data) {
loaderHide();
setTimeout(function() {
window.location.reload();
}, 1000);
}).
fail(function(data) {
loaderHide();
});
The all above works fine.
problem:
when i select 3 files. and im showing those 3 files in the preview.
again i select another 2 files, now preview shows total 5 files.
But in <input type="file" there will be only 2 files. i searched and found it is the default behavior.
Tried:
Preview image:
Preview code:
In the image source im having original image as base 64 encoded image. i need to send this image to php file. how can i do this?
<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
</head>
<body>
<input type="file" multiple accept="image/x-png,image/gif,image/jpeg" title="Upload Image" id="gallery_img" name="roomimage" />
<div class="gallery_section">
</body>
<script>
$(document).on('change','#gallery_img',function(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.*')) {
return;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
$('.gallery_section').append('<span class="imgPreview"><img id="image" name="image[]" class="imageThumb" src="'+e.target.result+'"><span class="remove icon-error" title="Close"></span></span>');
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
});
</script>
</html>
I am sending data via ajax to post in mysql. In this context how to inlude image file contents ?
If i use like :
var formData = document.getElementById("img_file");
alert(formData.files[0]);
, i get error . Note : img_file is the id of the file dom.
Any idea about this ?
You can use javascript FileReader for this purpose. Here is a sample code demonstration.
<html>
<body>
<input type="file" id="fileinput" />
<div id="ReadResult"></div>
<script type="text/javascript">
function readSingleFile(evt) {
//Retrieve the first (and only!) File from the FileList object
var f = evt.target.files[0];
if (f) {
var r = new FileReader();
r.onload = function (e) {
var contents = e.target.result;
document.getElementById("ReadResult").innerHTML = contents;
}
r.readAsText(f);
} else {
alert("Failed to load file");
}
}
document.getElementById('fileinput').addEventListener('change', readSingleFile, false);
</script>
</body>
</html>
Find more details here.
i think it may help you.
$('#image').change(function () {
$("#add").attr("disabled", true);
var img = this.files[0];
var reader = new FileReader;
reader.readAsDataURL(img);
reader.onload = function (e) {
var file = new Image;
file.src = e.target.result;
file.onload = function () {
$("#height").text(file.height);
$("#width").text(file.width);
$("#imageprev").attr("src", file.src);
$("#upld").removeAttr("disabled");
}
}
});
I have this JS code for uploading a .jpg file to firebase:
function handleFileSelect(evt) {
var f = evt.target.files[0];
var reader = new FileReader();
reader.onload = (function(theFile) {
return function(e) {
var filePayload = e.target.result;
// Generate a location that can't be guessed using the file's contents and a random number
var hash = CryptoJS.SHA256(Math.random() + CryptoJS.SHA256(filePayload));
var f = new Firebase(firebaseRef + 'pano/' + hash + '/filePayload');
spinner.spin(document.getElementById('spin'));
// Set the file payload to Firebase and register an onComplete handler to stop the spinner and show the preview
f.set(filePayload, function() {
spinner.stop();
document.getElementById("pano").src = e.target.result;
$('#file-upload').hide();
// Update the location bar so the URL can be shared with others
window.location.hash = hash;
});
};
})(f);
reader.readAsDataURL(f);
}
$(function() {
$('#spin').append(spinner);
var idx = window.location.href.indexOf('#');
var hash = (idx > 0) ? window.location.href.slice(idx + 1) : '';
if (hash === '') {
// No hash found, so render the file upload button.
$('#file-upload').show();
document.getElementById("file-upload").addEventListener('change', handleFileSelect, false);
} else {
// A hash was passed in, so let's retrieve and render it.
spinner.spin(document.getElementById('spin'));
var f = new Firebase(firebaseRef + '/pano/' + hash + '/filePayload');
f.once('value', function(snap) {
var payload = snap.val();
if (payload != null) {
document.getElementById("pano").src = payload;
} else {
$('#body').append("Not found");
}
spinner.stop();
});
}
});
My js imports:
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.8.3/jquery.min.js"></script>
<script src="//cdn.firebase.com/v0/firebase.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/spin.js/1.2.7/spin.min.js"></script>
<script src="sha256.js"></script>
<script src="firepano.js"></script>
My html looks something like this:
<input type="file" accept="image/*" capture="camera" id="file-upload">
Of course this is just a piece, but somehow the js function isn't triggered when I select a file, only if I put the HTML code outside of my file in a separate file and leave only this input tag. Is there some other piece of code that can interfier in the selectfile function trigger?
Try this:
<form action="" method="post" enctype="multipart/form-data">
<input type="file" name="image">
<input type="submit" value="Upload">
</form>
What techniques are used to load a file (ASCII or Binary) into a variable (var file = "text";) in JavaScript?
You want to use the new HTML5 File API and XMLHttpRequest 2.
You can listen to files being either selected via a file input or drag & dropped to the browser. Let's talk about the input[type="file"] way.
<input type="file">
Let's listen for files being selected.
var input; // let input be our file input
input.onchange = function (e) {
var files = input.files || [];
var file = files[0];
if (file) {
uploadFile(file);
}
};
What you need to create a real multipart file upload request is a FormData object. This object is a representation of the body of your HTTP POST request.
var uploadFile = function (file) {
var data = new FormData();
data.append('filename', file);
// create a HTTP POST request
var xhr = new XMLHttpRequest();
xhr.open('POST', './script.php', true);
xhr.send(data);
xhr.onloadend = function () {
// code to be executed when the upload finishes
};
};
You can also monitor the upload progress.
xhr.upload.onprogress = function (e) {
var percentage = 100 * e.loaded / e.total;
};
Ask if you need any clarification.
If you want to use the new HTML5 way this is how I did it... keep in mind that I made a method called File() and this is not a true HTML5 method its a wrapper to it... this might be changed in the future so beware (maybe rename it).
HTML:
<html>
<body>
<input type="file" id="files" name="file"/>
<button onclick="load()">Load File</button><br /><br />
<div id="content"></div>
<script>
function load() {
var fileObj = document.getElementById("files");
var fp = new File(fileObj);
fp.read(callback);
}
function callback(text) {
var content = document.getElementById("content");
content.innerHTML = text;
}
</script>
</body>
</html>
JavaScript:
function File(name) {
this.name = document.getElementById(name) ? document.getElementById(name).files : name.files ? name.files : name;
}
// Reads the file from the browser
File.prototype.read = function(callback) {
var files = this.name;
if (!files.length) {
alert('Please select a file!?');
return;
}
var file = files[0];
var reader = new FileReader();
reader.onloadend = function(evt) {
if (evt.target.readyState == FileReader.DONE) { // DONE == 2
callback(evt.target.result);
}
};
var data = file.slice(0, file.size);
reader.readAsBinaryString(data);
}
Have the JavaScript being generated inside a PHP or Rails (or whatever you use server-side) and include the file.
<?php
$my_string = file_get_contents('/path/to/file.txt');
?>
<script>
var my_js_file_string = "<?php echo $my_string; ?>";
...
document.write(my_js_file_string);
</script>