Προς το περιεχόμενο

JavaScript και html multiple file πεδίο - θέλω να περάσω όλα τα αρχεία


philos
Μετάβαση στην απάντηση Απαντήθηκε από Xvipes,

Προτεινόμενες αναρτήσεις

Δημοσ. (επεξεργασμένο)

Έχω τον ακόλουθο javascript κώδικα, χρησιμοποιώντας ένα plugin που λέγεται heic2any (κάνει convert μια εικόνα heic σε jpeg / δε μας ενδιαφέρει αυτή τη στιγμή, αλλού είναι το θέμα). Στο jQuery τρέχει noConflict γι αυτό βλέπετε έτσι τις μεταβλητές.

    function convertHeicToJpg(input)
    {
		let fileInputElement = jQuery(input)[0];
		let container = new DataTransfer();
		for (var i = 0; i < jQuery(input).get(0).files.length; ++i) 
		{
			var fileName = jQuery(input).get(0).files[i].name;
			var fileNameExt = fileName.substr(fileName.lastIndexOf('.') + 1);
			if(fileNameExt.toLowerCase() == "heic") 
			{
				var blob = jQuery(input).get(0).files[i];
				heic2any({
					blob: blob,
					toType: "image/jpeg",
				})
				.then(function (resultBlob) {
						var url = URL.createObjectURL(resultBlob);

						let file = new File([resultBlob], "test.jpg",{type:"image/jpeg", lastModified:new Date().getTime()});
						container.items.add(file);
						fileInputElement.files = container.files;
					})
					.catch(function (x) {
						console.log(x.code);
						console.log(x.message);
					});
			}			
		}
    }
	jQuery( document ).ready(function() {
	   jQuery("input[type=file]").change(function() {
			convertHeicToJpg(this);
		 });
	});

Το θέμα: αν επιλέγω 1 αρχείο, λειτουργεί κανονικά. Όμως το input type=file είναι multiple file selection.

Θέλω να ρωτήσω, τι τροποποιήσεις πρέπει να κάνει σε αυτό το part ή και αλλού:

let file = new File([resultBlob], "test.jpg",{type:"image/jpeg", lastModified:new Date().getTime()});
container.items.add(file);
fileInputElement.files = container.files;

... ώστε να λειτουργεί / περνάει όλα τα αρχεία στο fileInputElement, καθώς αυτή τη στιγμή ανεβάζει/ λαμβάνει υπόψιν του μόνο το ένα/ τελευταίο selection.

Καμιά ιδέα; :)

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Χωρίς να ξέρω ακριβώς πως λειτουργεί το παραπάνω, δεν θα μπορούσες να φτιάξεις μια function που θα παίρνει ως όρισμα το array με τα αρχεία και να τρέχει την παραπάνω συνάρτηση σε λούπα ώστε να τα κάνει ένα ένα; Ή μήπως δεν γίνεται να διαβάσεις ένα ένα τα αρχεία μέσα από το fileInputElement?

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Αυτό νομίζω το κάνω ήδη μέσω του ακόλουθου:

for (var i = 0; i < jQuery(input).get(0).files.length; ++i) 

Το θέμα μου είναι η επανατοποθέτηση των αρχείων μέσω του:

fileInputElement.files = container.files;

... που δεν δουλεύει για multiple files. Μόνο το τελευταίο λαμβάνει υπόψιν του. 😕

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Δοκίμασε να βάλεις αυτό: fileInputElement.files = container.files;  έξω από την for (μετά το τέλος της). 

Εσύ θέλεις όταν τελειώσει με όλα τα αρχεία το fileInputElements να έχει όλα τα files. Έτσι όπως το έχεις νομίζω αλλάζει κάθε φορά και ίσως να δημιουργείται πρόβλημα. 

Επεξ/σία από rafinos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

μμμ δεν δουλεύει καθόλου αν βγάλω έξω από την for τη συγκεκριμένη σειρά (το είχα δοκιμάσει). Ίσως παίζει θέμα (α)συγχρονισμού, καθώς παίρνει λίγα seconds μέχρι να φτάσει στο .then(function (resultBlob) ο κώδικας.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

To container στο τέλος της function περιέχει όλα τα αρχεία ή μόνο το τελευταίο; 
Αν έχει μόνο το τελευταίο δεν θα μπορούσε να γίνει κάτι τέτοιο κατά την εκχώρηση:
 

fileInputElement.files[i] = container.files;

ή καλύτερα:

fileInputElement.files[i] = file;

Μπορεί όλα αυτά που λέω να τα έχεις σκεφτεί, αλλά ποτέ δεν ξέρεις. 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημοσ. (επεξεργασμένο)

Το δοκίμασα αλλά δεν δουλεύει.

Μάλιστα αν βάλω αυτό το alert:

alert(i);
container.items.add(file);

... το i δεν είναι το σωστό (επιστρέφει πάντα 3 αν επιλέξω 3 αρχεία δλδ 0...1...2... για κάποιο λόγο πάει στο 3 και επιστρέφει 3 φορές το 3).

Αν βάλω το alert(i); απευθείς κάτω από τη for, φυσικά επιστρέφει 0...1...2.

Μετά από ψάξιμο είδα ότι δεν μπορούμε να βάλουμε μέσα σε then() το i γιατί το then ακολουθεί το ασύγχρονο μοντέλο.

Οπότε το πρόβλημα παραμένει :P

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • Λύση

Κάτι τέτοιο σου κάνει;

https://jsfiddle.net/v807r6t5/1/

Για να έχεις async i μπορείς με forEach (υπάρχουν και άλλοι τρόποι).

Μην το πάρεις copy/paste και περιμένεις να σου παίξει απλά δες την λογική και τροποποίησε το κατάλληλα.

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Το είπες και μόνος σου, καλείς την heic2any() που είναι ασύγχρονη σε λούπα. Η λύση είναι είτε promises, είτε χρήση async/await:

https://stackoverflow.com/questions/40328932/javascript-es6-promise-for-loop

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Στις 9/5/2023 στις 11:05 ΠΜ, Xvipes είπε

Κάτι τέτοιο σου κάνει;

https://jsfiddle.net/v807r6t5/1/

Για να έχεις async i μπορείς με forEach (υπάρχουν και άλλοι τρόποι).

Μην το πάρεις copy/paste και περιμένεις να σου παίξει απλά δες την λογική και τροποποίησε το κατάλληλα.

Σε ευχαριστώ για το jsfiddle!

Επειδή δεν ξέρω από Promises, το:

heic2any({
		blob: blob,
		toType: "image/jpeg",
	})
	.then

... που το βάζω στον κώδικά σου;

Έκανα διάφορες δοκιμές αλλά χωρίς επιτυχία. :)

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

2 minutes ago, philos said:

Σε ευχαριστώ για το jsfiddle!

Επειδή δεν ξέρω από Promises, το:

heic2any({
		blob: blob,
		toType: "image/jpeg",
	})
	.then

... που το βάζω στον κώδικά σου;

Έκανα διάφορες δοκιμές αλλά χωρίς επιτυχία. :)

Γραμμή 26 αντικαθηστάς το Promise 

 new Promise((resolve) => setTimeout(() => { resolve(blob); }, 1000))

με το 

heic2any({
		blob: blob,
		toType: "image/jpeg",
	})

 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Σε ευχαριστώ πολύ, δούλεψε!! :D

Το μόνο ότι χρειάστηκε να αλλάξω αυτό από το jsfiddle σου:

let fileInputElement = input.files[0];

... σε:

let fileInputElement = jQuery(input)[0];
  • Like 1
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

  • 3 εβδομάδες αργότερα...
Δημοσ. (επεξεργασμένο)

Παίδες καλησπέρα!

Μετά από crash tests, έχω εντοπίσει κι άλλο πρόβλημα στον κώδικα.

Ο τρέχοντας κώδικας είναι αυτός:

		function runSubmit()
		{
			setTimeout(function() { jQuery('#uploading').show(); jQuery('#do_upload_button_hidden').trigger('click'); }, 1000);
		}
		function getFileName(path)
		{
			var ext = path.substr(path.lastIndexOf('.') + 1);
			let newstring = path.match(/[-_\w]+[.][\w]+$/i)[0];
			return newstring.replace(ext, '').replace('.', '');
		}
        function convertHeicToJpg(input) 
		{
            let fileInputElement = jQuery(input)[0];
            let container = new DataTransfer();
			var length_files = input.files.length;

            Array.from(fileInputElement.files).forEach(function (item, i) {
                var fileName_str = item.name;
				var fileName = fileName_str.replace('(', '').replace(')', '');
                var fileNameExt = fileName.substr(fileName.lastIndexOf('.') + 1);
                if (fileNameExt.toLowerCase() == "heic") {

					jQuery('#converting_heic').show();			
                    var blob = item;
                    heic2any({
						blob: blob,
						toType: "image/jpeg",
						}).then(function (resultBlob) {
                            var url = URL.createObjectURL(resultBlob);
                            let file = new File([resultBlob], getFileName(fileName) +".jpg", { type: "image/jpeg", lastModified: new Date().getTime() });
                            
                            container.items.add(file);
                            fileInputElement.files = container.files;
                            console.log({ container, i });
							jQuery('#converting_heic').hide();
							
							if (length_files == (i + 1))
							{
								runSubmit();
							}							
                        })
                        .catch(function (x) {
                            console.log(x.code);
                            console.log(x.message);
                        });
                }
				else
				{
                  // ΕΙΝΑΙ JPG ΑΡΧΕΙΟ / ΜΗ ΤΟ ΠΕΙΡΑΞΕΙΣ!
                    container.items.add(item);
                    fileInputElement.files = container.files;	
					if (length_files == (i + 1))
					{
						runSubmit();
					}				
				}
            })
			
			
    }

    jQuery(document).ready(function () {
		jQuery('#do_upload_button').on('click', function () { convertHeicToJpg(jQuery("input[type=file]")[0]); });
    });

Το πρόβλημα στην Array.from(fileInputElement.files).forEach(function (item, i) είναι ότι αν ας πούμε έχουμε διαλέξει heic και jpg αρχεία και ας πούμε ότι το τελευταίο αρχείο είναι jpg (δλδ δεν μπαίνει στο if (fileNameExt.toLowerCase() == "heic"), και έχει φτάσει στο:

					if (length_files == (i + 1))
					{
						runSubmit();
					}	

... κάνει runSubmit() ενώ το heic conversion για τα προηγούμενα αρχεία ΔΕΝ έχει ολοκληρωθεί. Άρα δεν τα έχει τοποθετήσει ακόμα στο ".files".

Με άλλα λόγια η Array.from(fileInputElement.files).forEach(function (item, i) τρέχει ασταμάτησα και χωρίς να έχει ολοκληρωθεί το heic2any({}) προηγούμενων αρχείων, κάνει runSubmit() μιας που το τελευταίο αρχείο μπορεί να είναι jpg και έχει πάει στο else.

Καμιά ιδέα πως να βάλω - ίσως ένα έξτρα Promise() ή κάτι που να μην πηγαίνει παρακάτω αν δεν έχει ολοκληρωθεί το heic2any({}) ?

Θα ήθελα το runSubmit να τρέχει μόνο μόλις όλα τα heic αρχεία έχουν μετατραπεί και φυσικά να είναι ικανό το σύστημα να δεχθεί και heic και jpg αρχεία (τα jpg δε χρειάζεται να τα πειράξει, πάει αμέσως στο else).

Κάποιος μπορεί να διαλέξει στο input field μόνο jpg αρχεία (δε τα αγγίζουμε), μόνο heic (τα μετατρέπουμε) αρχεία ή μίξη heic και jpg αρχείων όπου δεν αγγίζει τα jpg και μετατρέπει τα heic.

Επεξ/σία από philos
Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δοκίμασες να κάνεις έλεγχο για το αν το length_files είναι ίσο με το fileInputElement.files.length και όχι με το i+1;

Επίσης εδώ με μπερδεύεις λίγο:
 

let fileInputElement = jQuery(input)[0]; //Αυτό
let container = new DataTransfer();
var length_files = input.files.length; //Και αυτό

//Γιατί αρχικά παίρνεις το jQuery(input)[0] ενώ κάτω κάνεις την καταχώρηση με σκέτο input; Μπορεί να ρωτάω και βλακείες αλλά οκ. 

Συνδέστε για να σχολιάσετε
Κοινοποίηση σε άλλες σελίδες

Δημιουργήστε ένα λογαριασμό ή συνδεθείτε για να σχολιάσετε

Πρέπει να είστε μέλος για να αφήσετε σχόλιο

Δημιουργία λογαριασμού

Εγγραφείτε με νέο λογαριασμό στην κοινότητα μας. Είναι πανεύκολο!

Δημιουργία νέου λογαριασμού

Σύνδεση

Έχετε ήδη λογαριασμό; Συνδεθείτε εδώ.

Συνδεθείτε τώρα
  • Δημιουργία νέου...