I want to upload multiple files and want to show their progress.I am trying the following code.
Here is my html code...
<div id='albumBox'>
<input type='file' multiple name='newsfeedAlbum[]' id='newsfeedAlbum' onchange='uploadNewsfeedImages()' />
<div>
<div id='uploadingImages'>
</div>
<div>
<preogress id='progressBar'> </progress>
</div>
<div>
<input type='button' id='albumButton' value='post' disabled />
</div>
here is my javascript code...
function uploadNewsfeedImages()
{
alert(document.getElementById("newsfeedAlbum").files.length);
var files = document.getElementById("newsfeedAlbum").files;
for(var i = 0;i < files.length;i++)
{
var file = files[i];
//alert("file name is "+files.item(i).name);
var formData = new FormData();
formData.append("image",file);
var xhr = new XMLHttpRequest();
xhr.open("POST","add_newsfeed.php",true);
alert(i);
xhr.upload.onprogress = function()
{
alert("bujji" + i);
}
xhr.send(formData);
}
}
function showUploadProgress(event)
{
var uploaded = event.loaded/event.total;
uploaded = Math.floor(uploaded*100);
document.getElementById("progressBar").value = uploaded;
}
But when I am trying to upload two images and alerting on upload.progress event it is alerting bujji2 and bujji2 instead of bujji0 and bujji1.How to handle individual upload.progress events....
It's a scope issue. The for loop will have completed by the time the onprogress events will trigger, so 'i' will be 2 every time the onprogress triggers because your 'for-loop' has finished running.
You can wrap the onprogress function in a closure to get the desired effect.
xhr.upload.onprogress = (function() {
return function() {
alert('bujji' + i);
};
}());
You need to save your i, because it will be changed before onprogress fired.
xhr.upload.fileNum = i;
xhr.upload.onprogress = function()
{
alert("bujji" + this.fileNum);
}
xhr.send(formData);
And read explanation for previous answer: Creating_closures_in_loops.3A_A_common_mistake
Related
I am creating a form with Remix, and I want upload images to Cloudinary.
I have here a Vanilla JS form example showing how to use the Web Fetch API to send images to Cloudinary: https://codepen.io/team/Cloudinary/pen/QgpyOK <— I would like to "transform" or "adapt" this code to work with Remix. Does anybody could help me, please?
I will paste the code I am working with so far.
On this code I've added already the HTML code from the CodePen link above. I just need to know how to exactly add the JS code, after the handleChange() I believe.
On the form element #fileElem we see there the onChange={handleFiles(this.files)} and this is what triggers the upload. My problem now, and because I am also already creating a post, is that I don't know exactly how/where to add all the handleFiles() code from the codepen link above.
On the code below you'll find comment blocks where I explain what I was did.
// import { isErrorResponse } from "#remix-run/react/data";
import { redirect, Form, useActionData, useTransition } from 'remix';
import { createPost } from '~/post';
export let action = async ({ request }) => {
let formData = await request.formData();
let title = formData.get('title');
let slug = formData.get('slug');
let markdown = formData.get('markdown');
let errors = {};
if (!title) errors.title = true;
if (!slug) errors.slug = true;
if (!markdown) errors.markdown = true;
if (Object.keys(errors).length) {
return errors;
}
await createPost({ title, slug, image, markdown });
return redirect('/admin');
};
export default function NewPost() {
let errors = useActionData();
let transition = useTransition();
let slug = '';
const handleChange = (e, document) => {
let text = e.target.value;
// using regex and replace, let's convert spaces to dashes
slug = text.replace(/\s/g, '-');
// lets set the value of the slug text box to be our new slug in lowercase
document.getElementById('slugInput').value = slug.toLowerCase();
};
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - added bellow the JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
const cloudName = 'demo';
const unsignedUploadPreset = 'doc_codepen_example';
var fileSelect = document.getElementById('fileSelect'),
fileElem = document.getElementById('fileElem'),
urlSelect = document.getElementById('urlSelect');
fileSelect.addEventListener(
'click',
function (e) {
if (fileElem) {
fileElem.click();
}
e.preventDefault(); // prevent navigation to "#"
},
false
);
urlSelect.addEventListener(
'click',
function (e) {
uploadFile('https://res.cloudinary.com/demo/image/upload/sample.jpg');
e.preventDefault(); // prevent navigation to "#"
},
false
);
// ************************ Drag and drop ***************** //
function dragenter(e) {
e.stopPropagation();
e.preventDefault();
}
function dragover(e) {
e.stopPropagation();
e.preventDefault();
}
dropbox = document.getElementById('dropbox');
dropbox.addEventListener('dragenter', dragenter, false);
dropbox.addEventListener('dragover', dragover, false);
dropbox.addEventListener('drop', drop, false);
function drop(e) {
e.stopPropagation();
e.preventDefault();
var dt = e.dataTransfer;
var files = dt.files;
handleFiles(files);
}
// *********** Upload file to Cloudinary ******************** //
function uploadFile(file) {
var url = `https://api.cloudinary.com/v1_1/${cloudName}/upload`;
var xhr = new XMLHttpRequest();
var fd = new FormData();
xhr.open('POST', url, true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
// Reset the upload progress bar
document.getElementById('progress').style.width = 0;
// Update progress (can be used to show progress indicator)
xhr.upload.addEventListener('progress', function (e) {
var progress = Math.round((e.loaded * 100.0) / e.total);
document.getElementById('progress').style.width = progress + '%';
console.log(`fileuploadprogress data.loaded: ${e.loaded}, data.total: ${e.total}`);
});
xhr.onreadystatechange = function (e) {
if (xhr.readyState == 4 && xhr.status == 200) {
// File uploaded successfully
var response = JSON.parse(xhr.responseText);
// https://res.cloudinary.com/cloudName/image/upload/v1483481128/public_id.jpg
var url = response.secure_url;
// Create a thumbnail of the uploaded image, with 150px width
var tokens = url.split('/');
tokens.splice(-2, 0, 'w_150,c_scale');
var img = new Image(); // HTML5 Constructor
img.src = tokens.join('/');
img.alt = response.public_id;
document.getElementById('gallery').appendChild(img);
}
};
fd.append('upload_preset', unsignedUploadPreset);
fd.append('tags', 'browser_upload'); // Optional - add tag for image admin in Cloudinary
fd.append('file', file);
xhr.send(fd);
}
// *********** Handle selected files ******************** //
var handleFiles = function (files) {
for (var i = 0; i < files.length; i++) {
uploadFile(files[i]); // call the function to upload the file
}
};
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
// ATTEMPT - end of JS code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
return (
<Form method='post'>
<p>
<label htmlFor=''>
Post Title: {errors?.title && <em>Title is required</em>}{' '}
<input onChange={handleChange} type='text' name='title' />
</label>
</p>
<p>
<label htmlFor=''>
{' '}
Post Slug: {errors?.slug && <em>Slug is required</em>}
<input placeholder={slug} id='slugInput' type='text' name='slug' />
</label>
</p>
<p>
<label htmlFor='markdown'>Markdown:</label>{' '}
{errors?.markdown && <em>Markdown is required</em>}
<br />
<textarea name='markdown' id='' rows={20} cols={30} />
</p>
<p>
<button type='submit'>
{transition.submission ? 'Creating...' : 'Create Post'}
</button>
</p>
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
{/* // ATTEMPT - added HTML form code from codepen !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! */}
<div id='dropbox'>
<h1>Client-Side Upload to Cloudinary with JavaScript</h1> Learn more in
this blog post -{' '}
<a href='https://cloudinary.com/blog/direct_upload_made_easy_from_browser_or_mobile_app_to_the_cloud'>
Direct upload made easy from browser or mobile app to the cloud
</a>
<div className='form_line'>
<h4>
Upload multiple files by clicking the link below or by dragging and
dropping images onto the dashed region
</h4>
<div className='form_controls'>
<div className='upload_button_holder'>
<input
type='file'
id='fileElem'
multiple
accept='image/*'
style={{ display: 'none' }}
onChange={handleFiles(this.files)}
/>
<a href='#' id='fileSelect'>
Select some files
</a>
<a href='#' id='urlSelect'>
URL Upload
</a>
</div>
</div>
</div>
<div className='progress-bar' id='progress-bar'>
<div className='progress' id='progress'></div>
</div>
<div id='gallery' />
</div>
{/* // end of ATTEMPT */}
{/* // end of ATTEMPT */}
{/* // end of ATTEMPT */}
</Form>
);
}
I've also found this post about this topic, but the code seems to be gone forever from the internet: https://twitter.com/remix_run/status/1494096161669464064?lang=en
Thank you!
The remix example was moved here
I have a file input which then preview the images after adding. The images show, but I get:
Uncaught TypeError: Failed to execute 'readAsDataURL' on 'FileReader': parameter 1 is not of type 'Blob'."
What's causing this, surely they wouldn't show still?
$('#image-upload-input').on('change', function() {
var files = document.getElementById('image-upload-input').files;
for (var key in files) {
if (files[key]) {
var reader = new FileReader();
reader.onload = function(e) {
$('.image-upload-container').append('<img src="'+ e.target.result +'" style="width: 100px;">');
}
reader.readAsDataURL(files[key]);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="image-upload-input" type="file" multiple>
<div class="image-upload-container"></div>
You are misusing the for( in ) loop. As it iterates it chokes on the length property - which is not a Blob Object. This happens because the for( in ) iterates over all (enumerable) object properties and not just "own properties". Reference
You have two choices:
Stick to the traditional (and always works) for() loop
Use the for( of ) loop
The for( of ) loop will only iterate "own properties" while the traditional for() loop will always, always work when a length property is available.
$('#image-upload-input').on('change', function() {
var files = document.getElementById('image-upload-input').files;
for(file of files) {
if (file) {
var reader = new FileReader();
reader.onload = function(e) {
$('.image-upload-container').append('<img src="'+ e.target.result +'" style="width: 100px;">');
}
reader.readAsDataURL(file);
}
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="image-upload-input" type="file" multiple>
<div class="image-upload-container"></div>
Good example of few issues together.
Exception you get - is because files isn't real array, so for ... in - iterates over "0", "1"... "item","length" keys.
You can't use async function inside loop without isolating the scope
My personal opinion: don't use jQuery if you can :-)
$('#image-upload-input').on('change', function() {
var files = document.getElementById('image-upload-input').files;
for (var key=0; key < files.length; key++) {
(function(){
var reader = new FileReader();
var file = files[key];
reader.onload = function(e) {
var img = document.createElement('img');
img.style= "width:100px";
img.src = reader.result;
$('.image-upload-container').append($(img));
}
reader. readAsDataURL(file);
})();
}
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id="image-upload-input" type="file" multiple>
<div class="image-upload-container"></div>
I would have ditched the FileReader for URL.createObjectURL and just use a regular for loop
$('#image-upload-input').on('change', function() {
var files = document.getElementById('image-upload-input').files;
for (var i = 0; i < files.length; i++) {
var url = URL.createObjectURL(files[i]);
$('.image-upload-container').append('<img src='+ url +' style="width: 100px;">');
}
});
and possible added this extra attribute to the input
accept="image/*"
I'm working on a project with the following steps:
Creating the form with multiple images upload
Previewing, remove images on queue before upload
Handling the file input value before submit
Here is my code
var fileList = [];
function toObject(arr) {
var rv = {};
for (var i = 0; i < arr.length; ++i)
rv[i] = arr[i];
return rv;
}
//Image prev
// Multiple images preview in browser
var imagesPreview = function (input, imageContainer) {
if (input.files) {
var filesAmount = input.files.length;
$(imageContainer).html('');
for (i = 0; i < filesAmount; i++) {
var reader = new FileReader();
reader.onload = function (event) {
var html = '<div class="image-item col-sm-6 col-md-4 col-lg-3"><div class="image-wrapper">' +
' <img src="'
+ event.target.result + '"/></div></div>';
$(html).appendTo(imageContainer);
}
var files = input.files;
fileList.push(files[i]);
reader.readAsDataURL(input.files[i]);
}
input.files = toObject(fileList);
}
};
$('#input-image').on('change', function () {
imagesPreview(this, '.image-container');
});
<div class="image-item">
<!-- input the image from user -->
<input id="input-image" type="file" name="photos[]" multiple>
<hr>
<div class="image-container row">
<!-- Previewing the image thumbnail -->
</div>
</div>
My questions: Can I set the value of the input-image with fileList variable because I set it but error occurs
Sry, a bit tired, will not go into depth or solve the hole thing...
There is only one way to change the value of file inputs and that is with another FileList instance. the only way you can get them is with some new api's so it won't work in all browsers. I made a function to help you with that.
var fileList = [];
// only way to change input[type=file] value is with a other FileList instance
// and this is the only way to construct a new FileList
function createFileList(a) {
a = [].slice.call(Array.isArray(a) ? a : arguments)
for (var c, b = c = a.length, d = !0; b-- && d;) d = a[b] instanceof File
if (!d) throw new TypeError('expected argument to FileList is File or array of File objects')
for (b = (new ClipboardEvent('')).clipboardData || new DataTransfer; c--;) b.items.add(a[c])
return b.files
}
// This is what you got to do later when they remove a image
//
// this will trigger a change event so you maybe want to temporary
// turn off the change event listener
//
// input.files = createFileList(fileList)
// Image prev
// Multiple images preview in browser
function imagesPreview(input, imageContainer) {
$(imageContainer).empty();
var URL = window.URL || window.webkitURL;
var $html = $(
'<div class="image-item col-sm-6 col-md-4 col-lg-3">'+
'<div class="image-wrapper"> <img></div></div>'
);
$.each(input.files, function(i, file) {
var $clone = $html.clone()
// could be a good idea to revoke url also
$clone.find('img').attr('src', URL.createObjectURL(file))
$clone.appendTo(imageContainer);
fileList.push(file);
});
}
$('#input-image').on('change', function () {
imagesPreview(this, '.image-container');
});
The other solution is to append each file into a formdata and send the form using ajax
I'm trying to find a solution for my previous post: Mongo gives duplicate key error on _id_ field in Meteor application
In order to do that I want to read data from my CSV file at server side, and not from the client.
First I tried the solution in this post Using node-csv and meteor-file to import CSV into a collection but meteor-file isn't compatible anymore with current version of Meteor. I also tried the solution in this post Upload Data to Meteor / Mongo DB but It's also on the client and that solution generates the same error as in my previous post.
After some further research I tried to read the data with the code below. However it doesn't work:
First I created a collection:
Meteor.orders = new Meteor.Collection('Orders');
I defined the following template to read the csv file:
<template name="read_file_orders">
<form class="well form-inline">
<label class="control-label" for="fileInput2">Kies bestand</label>
<input class="input-file" id="fileInput2" type="file" name="files[]">
<Button class="btn btn-primary" id="read_orders">Importeer</button>
<button class="btn btn-danger" id="erase_orders">Wis gegevens</button>
</form>
</template>
This is the client javascript:
Template.read_file_orders.events({
"click #read_orders" : function(e) {
var f = document.getElementById('fileInput2').files[0];
console.log("read file");
readFile(f, function(content) {
Meteor.call('upload',content);
});
}
});
readFile = function(f,onLoadCallback) {
var reader = new FileReader();
reader.onload = function (e){
var contents=e.target.result
onLoadCallback(contents);
}
reader.readAsText(f);
};
And this is the server javascript:
Meteor.startup(function () {
// code to run on server at startup
return Meteor.methods({
upload : function(fileContent) {
console.log("start insert");
import_file_orders(fileContent);
console.log("completed");
}
});
});
import_file_orders = function(file) {
var lines = file.split('%\r\n');
var l = lines.length - 1;
for (var i=0; i < l; i++) {
var line = lines[i];
var line_parts = line.split('|');
var ex_key = line_parts[0];
var ex_name = line_parts[1];
var clin_info = line_parts[2];
var order_info = line_parts[3];
var clinician_last_name = line_parts[4];
var clinician_first_name = line_parts[5];
var clinician_code = line_parts[6];
var clinician_riziv = line_parts[7]
var pat_id = line_parts[8];
Meteor.orders.insert({Patient:pat_id, Exam_code:ex_key, Exam_name:ex_name, Clinical_info:clin_info, Order_info:order_info, Clinician_first:clinician_first_name, Clinician_last:clinician_last_name, Clinician_c_code:clinician_code, Clinician_riziv:clinician_riziv, Planned:null});
console.log("%");
};
When I try reading the file, nothing happens. Only the console logs appear in the server console but there is nothing imported. Even the Orders collection isn't created.
It's clear that I'm doing something wrong but I don't know what exactly. However I think the solution isn't too far. Maybe someone of you can show me the right direction?
Kind regards
EDIT:
In order to revmen's answer here's the full code of my testapp:
test.html:
<head>
<title>test</title>
</head>
<body>
<h1>Welcome to Meteor!</h1>
{{> read_file_orders}}
</body>
<template name="read_file_orders">
<form class="well form-inline">
<label class="control-label" for="fileInput2">Kies bestand</label>
<input class="input-file" id="fileInput2" type="file" name="files[]">
<Button class="btn btn-primary" id="read_orders">Importeer</button>
<button class="btn btn-danger" id="erase_orders">Wis gegevens</button>
</form>
</template>
test.js
Orders = new Mongo.Collection("orders");
if (Meteor.isClient) {
// counter starts at 0
Template.read_file_orders.events({
"click #read_orders" : function(e) {
var f = document.getElementById('fileInput2').files[0];
console.log("read file");
readFile(f, function(content) {
Meteor.call('upload',content);
});
}
});
import_file_orders = function(file) {
console.log("enter function import_file_orders")
var lines = file.split(/\r\n|\n/);
var l = lines.length - 1;
for (var i=0; i < l; i++) {
var line = lines[i];
var line_parts = line.split(',');
var ex_key = line_parts[0];
var ex_name = line_parts[1];
var clin_info = line_parts[2];
var order_info = line_parts[3];
var clinician_last_name = line_parts[4];
var clinician_first_name = line_parts[5];
var clinician_code = line_parts[6];
var clinician_riziv = line_parts[7]
var pat_id = line_parts[8];
var result = Orders.insert({Patient:pat_id, Exam_code:ex_key, Exam_name:ex_name, Clinical_info:clin_info, Order_info:order_info, Clinician:{first:clinician_first_name, last:clinician_last_name, c_code:clinician_code, riziv:clinician_riziv}, Planned:null});
console.log(Orders.findOne(result));
};
}
readFile = function(f,onLoadCallback) {
//When the file is loaded the callback is called with the contents as a string
var reader = new FileReader();
reader.onload = function (e){
var contents=e.target.result
onLoadCallback(contents);
}
reader.readAsText(f);
};
}
if (Meteor.isServer) {
Meteor.startup(function () {
// code to run on server at startup
});
Meteor.methods({
upload : function(fileContent) {
console.log("start insert");
import_file_orders(fileContent);
console.log("completed");
}
});
}
You were very close. I just had to make a few changes to get it working.
I don't know what your .csv file looks like, so I made one that's like this:
A1, B1, C1, D1, E1, F1, G1, H1, I1
A2, B2, C2, D2, E2, F2, G2, H2, I2
Your file.split operation wasn't splitting the lines, but was putting everything on one big line. I did it this way and it worked:
var lines = file.split(/\r\n|\n/);
That got individual lines to split into members of the array. Then I assumed that, since you're calling your input a CSV, your values are separated by commas, not pipes. So I changed your line.split to this
var line_parts = line.split(',');
Other changes I made may not be what was causing yours to fail, but this is how I think things are normally done...
Instead of declaring your collection like this
Meteor.orders = new Meteor.Collection('Orders');
I did it like this
Orders = new Mongo.Collection("orders");
Note that this is run by both the server and the client.
Instead of your way of declaring methods on the server, I just put this into server code (not in Meteor.start):
Meteor.methods({
upload : function(fileContent) {
console.log("start insert");
import_file_orders(fileContent);
console.log("completed");
}
});
And, of course, I changed the insert line at the bottom of your import_file_orders function
var result = Orders.insert({Patient:pat_id, Exam_code:ex_key, Exam_name:ex_name, Clinical_info:clin_info, Order_info:order_info, Clinician_first:clinician_first_name, Clinician_last:clinician_last_name, Clinician_c_code:clinician_code, Clinician_riziv:clinician_riziv, Planned:null});
console.log(Orders.findOne(result));
EDIT for updated code in the question:
Move the import_file_orders function from the client block to the server block.
I have a bunch of forms on a page that allow a user to edit information for each respective form. One of the inputs for the form is an image upload.
The forms are of the form below:
<form class="myForm" ...>
<div class="imagePreview"></div>
<input type="file" name="myImage" onchange="handleFiles(this.files)" />
</form>
And I have javascript to handle the image preview as follows:
function handleFiles(files) {
$(".obj").remove();
for (var i = 0; i < files.length; i++) {
var file = files[i];
var imageType = /image.*/;
if (!file.type.match(imageType)) {
continue;
}
var pic_div = document.getElementById("imagePreview");
var img = document.createElement("img");
img.classList.add("obj");
img.file = file;
pic_div.appendChild(img);
var reader = new FileReader();
reader.onload = (
function(aImg) {
return function(e) {
aImg.src = e.target.result;
};
}
)(img);
reader.readAsDataURL(file);
}
}
I want to replace the line:
var pic_div = document.getElementById("imagePreview");
with the appropriate line. This is where I am getting confused. I don't know how to refer to the div of class "imagePreview" for THIS FORM of class myForm.
Any help is much appreciated.
The problem is that you're getting the div with the Id imagePreview, when the div in the form have the imagePreview CSS class, what you can do is either give the div the required id, more less like this:
<div id="imagePreview"></div>
Or, if you will have multiple divs with the same class get them using jQuery like this:
$(".imagePreview").each(function(index){
//Do Something
});
Or:
var pic_div = $(".imagePreview")[0]