FileList, push new image files, access/remove by index - javascript

This input element allows to take multiple images, for example if i select 2 images i get: FileList {0: File, 1: File, length: 2}. Then if i select again for example 1 image i get another FileList: FileList {0: File, length: 1}. There's a chance to push this new image to the first FileList? And it's possible to remove an image of the FileList by his index? Thanks in advance.
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
console.log(files);
for (var i = 0; i < files.length; i++) {
console.log(files[i]);
}
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
if (!f.type.match('image.*')) {
continue;
}
let reader = new FileReader();
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);
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
.thumb {
height: 75px;
border: 1px solid #000;
margin: 10px 5px 0 0;
}
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>

You are selecting only a single file 3 times.
To get multiple files you need to select them in a single call from the file-picker.
It will depend on the OS, but on my macOS I can do so by pressing the cmd key (probably ctrl on other OSes) while clicking on the items I want to select.

This is the way you can achieve your desired result using FormData.
You can change the code to achieve your desired look and functionality.
In summary what the following code does:
Creates a new FormData instance
Accepts files from file input
Appends the file to a field named file-${index} field of FormData created in step 1
Creates a FileReader instance for each File
The FileReader reads the file content and returns data: URL representing the file's data as a base64 encoded string.
Creates a span element and appends an img element to it with the fileContent of step 5 as src
Attaches the click listener to the span so when the user clicks an image the corresponding file will be removed from FormData of step 1 and the span will be removed from DOM
The FormData will be sent to the backend as a request body using Fetch API when the user clicks the submit files button using the sendFiles function as click handler.
you can see the list of files attached to FormData with their corrponding form field name and original filename under sunbmit button as a ul generated using listFiles function
const formData = new FormData();
// to be used for files field names of FormData
let index = 0;
// for listing current files
const listFiles = () => {
const list = document.createElement("ul");
Array.from(formData.entries()).forEach((entry) => {
const item = document.createElement("li");
item.innerHTML = entry[0] + ": " + entry[1].name;
list.appendChild(item);
});
document.querySelector("#fileList").innerHTML = list.innerHTML;
};
// for sending files to the backend
const sendFiles = () => {
fetch("/upload/path", {
body: formData,
method: "POST",
})
.then((response) => response.json()) // If the response is in JSON format
.then((data) => {
console.log(data);
})
.catch((error) => {
console.error(error);
});
};
// for outputting fileReader output and file for FormData
// it needs to be async because of async nature of fileReader onload event so we can keep FormData and FileReader in sync using index
const readFile = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.onload = (event) => {
const theFile = event.target;
return resolve([file, theFile]);
};
fileReader.readAsDataURL(file);
});
};
const handleFileSelect = async (event) => {
var files = event.target.files;
for (const file of files) {
if (file.type.match("image.*")) {
const [fileData, theFile] = await readFile(file);
const id = `file-${index}`;
formData.append(id, fileData);
const span = document.createElement("span");
const img = document.createElement("img");
img.src = theFile.result;
img.alt = "Image thumb";
img.title = escape(fileData.name);
img.className = "thumb";
span.appendChild(img);
// for removing the thumbnail and its linked file from FormData
span.addEventListener("click", () => {
formData.delete(id);
// listing current files appended to FormData after removing this thumbnail
listFiles();
span.remove();
});
index++;
document.getElementById("list").insertBefore(span, null);
}
}
// list files after adding new file/files
listFiles();
};
document.getElementById("files").addEventListener("change", handleFileSelect, false);
.thumb {
height: 75px;
border: 1px solid #000;
margin: 10px 5px 0 0;
}
<input type="file" id="files" name="files[]" multiple />
<br />
<h3>click on images to remove them</h3>
<output id="list"></output>
<br /><br /><br />
<button onclick="sendFiles()">submit Files</button>
<ul id="fileList"></ul>
example of How to upload a file using Fetch for refrence.

Related

how should I go by making a audio file converter in react

I am trying to make an audio file converter that lets a user submit a file. Then uses JavaScripts Web Audio API to convert the pitch and stretch the file. I have gotten as far as uploading the file, use file reader to onload a function that stretches and converts the pitch. Now I am trying to export that file with the changes and I can right now only download the original file but not with the changes. I dont know how to assign file = buffer because it's from another class. How should I got by making this happen?
convertFile () {
var fileInput = document.getElementById('audio-file')
var ctx = new AudioContext()
var convertFiles = document.getElementById('convert_button')
//load audio file listener
convertFiles.addEventListener("click", function() {
if (fileInput.files[0] == undefined) {
console.log("no file found")
return false
}
var reader1 = new FileReader()
reader1.onload = function(ev) {
ctx.decodeAudioData(ev.target.result). then(function(buffer){
var soundSource = ctx.createBufferSource()
soundSource.buffer = buffer
// create the stretch
soundSource.playbackRate.linearRampToValueAtTime(0.0185, ctx.currentTime)
//connect source
soundSource.connect(ctx.destination)
// convert pitch
var pitchChange = ctx.createBiquadFilter()
pitchChange.type = 'highpass'
pitchChange.frequency.value = 432
pitchChange.connect(ctx.destination)
})
}
reader1.readAsArrayBuffer(fileInput.files[0])
})
let file = fileInput.files[0]
let url = URL.createObjectURL(file)
let link = document.createElement('a')
link.href = url
link.download = file.name
link.click()
link = null
URL.revokeObjectURL(url)
}
render() {
return (
<div className="sec2">
<input type="file" id="audio-file" accept="audio/mpeg, audio/ogg, audio/*" name="file" onChange={this.uploadFile} />
<button type="button" id="convert_button" onClick={this.convertFile}>Convert to 432Hz</button>
<download onClick={this.downloadFile}>Download File</download>
</div>
)
}
}
export default ConverterSec2
I started looking into this... I fixed a couple issues such as the audio file being loaded twice. However this is work in progress answer... I haven't figure out the saving part yet.
class ConverterSec2 extends React.Component {
uploadFile = ({ target: { files } }) => {
console.log(files[0])
let data = new FormData()
data.append('file', files[0])
}
convertFile () {
var fileInput = document.getElementById('audio-file')
var ctx = new AudioContext()
var convertFiles = document.getElementById('convert_button')
//load audio file listener
if (fileInput.files[0] == undefined) {
console.log("no file found")
return false
}
var soundSource = ctx.createBufferSource();
var reader1 = new FileReader()
reader1.onload = function(ev) {
ctx.decodeAudioData(ev.target.result).then(function(buffer){
soundSource.buffer = buffer
// create the stretch
soundSource.playbackRate.linearRampToValueAtTime(0.0185, ctx.currentTime)
//connect source
soundSource.connect(ctx.destination)
// convert pitch
var pitchChange = ctx.createBiquadFilter()
pitchChange.type = 'highpass'
pitchChange.frequency.value = 432
pitchChange.connect(ctx.destination)
})
}
reader1.readAsArrayBuffer(fileInput.files[0]);
}
downloadFile() {
let fileInput = document.getElementById('audio-file')
let file = fileInput.files[0]
let url = URL.createObjectURL(file)
let link = document.createElement('a')
link.href = url
link.download = file.name
link.click()
link = null
URL.revokeObjectURL(url)
}
render() {
return (
<div className="sec2">
<input type="file" id="audio-file" accept="audio/mpeg, audio/ogg, audio/*" name="file" onChange={this.uploadFile} />
<button type="button" id="convert_button" onClick={this.convertFile}>Convert to 432Hz</button>
<button onClick={this.downloadFile}>Download File</button>
</div>
)
}
}
Live Demo

How to get all url's image from a multiple input file?

I'm trying to get all url values from a multiple input file. It works correctly with just one image but if I try to attach more than one, I retrieve a null response and only get the last one correctly. I hope anybody could help with this issue!
Post my code below and JSFiddle example:
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>
<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) {
//show url image
var dataURL = reader.result;
alert(dataURL);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
</script>
https://jsfiddle.net/3q49moyj/

Using the FileReader API

I am trying to read a local static file using the FileReader API but for some reason it's not reading anything. I am using:
var file = new File(
[""],
"config.toml"
);
var reader = new FileReader();
reader.onload = event => {
console.log(reader.result);
};
reader.readAsText(file);
The result is just empty. What am I doing wrong? Also, how will Javascript know where to look for config.toml?
EDIT: To clarify, I want to read the file from the server where the application is hosted not from the client machine.
In your code you are using File constructor which returns a newly constructed File, where the arguments are as follows:
var myFile = new File(bits, name[, options]);
bits An Array of ArrayBuffer, ArrayBufferView, Blob, or DOMString objects — or a mix of any such objects. This is the file content encoded as UTF-8
name A USVString representing the file name or the path to the file
It does read the file from the path you supply. So it will read the content you've passed to it, and you've passed to it an empty string.
If you want to read a file from file system you will need to use an input element with type attribute set to file and browse for it.
Here is an sample how to use the FileReader api to read images:
function handleFileSelect(evt) {
var files = evt.target.files;
// 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);
.thumb {
height: 75px;
border: 1px solid #000;
margin: 10px 5px 0 0;
}
<input type="file" id="files" name="files[]" multiple />
<output id="list"></output>

Error: Invalid or corrupted PDF file Firefox

I am trying to upload a PDF file and once done uploading, I am trying to show that PDF file in an IFrame with the stream content I have with me in scope. Same code works if I upload an image file but throws below error for PDF upload in Firefox.
Error
Invalid or corrupted PDF file.
PDF.js v1.9.583 (build: d7b37ae7)
Message: Invalid PDF structure
viewer.js:1359:7
Error: Invalid or corrupted PDF file.
HTML
<input type="file" id="files" name="files[]" multiple />
<iframe id="uploadedFileIframe" title="PDF in an i-Frame" src="" scrolling="auto" height="400" width="400" />
CSS
.thumb {
height: 75px;
border: 1px solid #000;
margin: 10px 5px 0 0;
}
JavaScript
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('pdf.*')) {
continue;
}
var reader = new FileReader();
// Closure to capture the file information.
reader.onload = (function(theFile) {
return function(e) {
// Render thumbnail.
var enc = btoa(unescape(encodeURIComponent( e.target.result)))
document.getElementById('uploadedFileIframe').setAttribute('src','data:application/pdf;base64,'+ enc);
};
})(f);
// Read in the image file as a data URL.
reader.readAsDataURL(f);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
It looks like you've overengineered the solution a little bit
document.getElementById('uploadedFileIframe').src = reader.result;
should work if you place it instead of
var enc = btoa(unescape(encodeURIComponent( e.target.result)))
document.getElementById('uploadedFileIframe').setAttribute('src','data:application/pdf;base64,'+ enc);
https://jsfiddle.net/kc8sdas5/
You are accepting multiple files to be selected and you are always replacing the same iframe with a new source. You don't create a new iframe for each file. Also you don't have to read the file as a base64 string, it's pointless and also a bad for performances and memory. You could instead use the URL.createObjectURL instead, which is syncron, so you can get rid of your closure
function handleFileSelect(evt) {
var files = evt.target.files; // FileList object
var previews = document.querySelector('#previews');
var iframe;
var URL = window.URL || webkitURL;
// Loop through the FileList and render image files as thumbnails.
for (var i = 0, f; f = files[i]; i++) {
// Only process pdf files.
if (!f.type.match('pdf.*')) {
continue;
}
iframe = document.createElement('iframe')
iframe.title = "PDF in an i-Frame";
iframe.scrolling = "auto";
iframe.width = iframe.hight = 400;
iframe.src = URL.createObjectURL(f);
previews.appendChild(iframe);
}
}
document.getElementById('files').addEventListener('change', handleFileSelect, false);
<input type="file" id="files" name="files[]" multiple />
<div id="previews"></div>

Uploading a JSON file and using it

How can I upload a JSON file on some click on a button on my web page say "import", and use it to store in a variable to use and update it using JavaScript.
I have gone through the other posts but could not find any answer.
I am saving the JSON variable using this function:
function save(filename, data){
if(!data) {
alert('error : No data')
return;
}
if(typeof data === "object"){
data = JSON.stringify(data, undefined, 4)
}
var blob = new Blob([data], {type: 'text/json'}),
e = document.createEvent('MouseEvents'),
a = document.createElement('a')
a.download = filename
a.href = window.URL.createObjectURL(blob)
a.dataset.downloadurl = ['text/json', a.download, a.href].join(':')
e.initMouseEvent('click', true, false, window, 0, 0, 0, 0, 0, false, false, false, false, 0, null)
a.dispatchEvent(e)
}
This is working fine and it downloads the file on clicking another button say "export".
How upload this file back and make a JSON variable of this file data?
Without server side code, your best approach may be to provide a textarea element for the user to copy/paste the JSON into, and then parse it using JSON.parse.
You could even go as far as to use something like Ace Editor to provide syntax highlighting for JSON - you can see it in action on the Ace Editor Kitchen Sink Demo - select JSON from the dropdown list in the top left.
Edit:
Turns out I was wrong. Here is a fiddle demonstrating the FileReader in use, which is exactly what you need:
https://jsfiddle.net/Ln37kqc0/
Here is the code:
Javascript:
document.getElementById('import').onclick = function() {
var files = document.getElementById('selectFiles').files;
console.log(files);
if (files.length <= 0) {
return false;
}
var fr = new FileReader();
fr.onload = function(e) {
console.log(e);
var result = JSON.parse(e.target.result);
var formatted = JSON.stringify(result, null, 2);
document.getElementById('result').value = formatted;
}
fr.readAsText(files.item(0));
};
HTML:
<input type="file" id="selectFiles" value="Import" /><br />
<button id="import">Import</button>
<textarea id="result"></textarea>
I have got a way to use the uploaded json file, here is the way i found.
$("#inputFile").change(function(e) {
onChange(e);
});
function onChange(event) {
var reader = new FileReader();
reader.onload = onReaderLoad;
reader.readAsText(event.target.files[0]);
}
function onReaderLoad(event){
//alert(event.target.result);
var obj = JSON.parse(event.target.result);
alert(obj);
}
Basic upload File:
<input id="contentFile" type="file" accept="application/json" />
document.getElementById('contentFile').onchange = function(evt) {
try {
let files = evt.target.files;
if (!files.length) {
alert('No file selected!');
return;
}
let file = files[0];
let reader = new FileReader();
const self = this;
reader.onload = (event) => {
console.log('FILE CONTENT', event.target.result);
};
reader.readAsText(file);
} catch (err) {
console.error(err);
}
}
[2021] Promise based approach
As mentioned here, you can make use of the newer blob api to easily get the file's value via:
await blob.text()
const getJsonUpload = () =>
new Promise(resolve => {
const inputFileElement = document.createElement('input')
inputFileElement.setAttribute('type', 'file')
inputFileElement.setAttribute('multiple', 'true')
inputFileElement.setAttribute('accept', '.json')
inputFileElement.addEventListener(
'change',
async (event) => {
const { files } = event.target
if (!files) {
return
}
const filePromises = [...files].map(file => file.text())
resolve(await Promise.all(filePromises))
},
false,
)
inputFileElement.click()
})
document.getElementById('upload-button').onclick = async () => {
const jsonFiles = await getJsonUpload()
console.log({jsonFiles})
}
<button id="upload-button">
Upload
</button>
Try this, works perfect
handleUploadFile = async(doc) => {
let file = doc.target.files[0]
let reader = new FileReader(file)
// await reader.readAsDataURL(file)
reader.readAsText(file)
reader.onload = async(e) => {
let aaa = e.target.result
let content = await JSON.parse(aaa)
console.log(content)
}
}
With the newer Blob API, the current top answer can be simplified by skipping the FileReader:
document.getElementById('import').onclick = function() {
var files = document.getElementById('selectFiles').files;
console.log(files);
if (files.length <= 0) {
return false;
}
files[0].text().then(function(text) {
console.log(text);
var result = JSON.parse(text);
var formatted = JSON.stringify(result, null, 2);
document.getElementById('result').value = formatted;
})
};
To explain the code a little: files[0] itself is a File object, which inherits from Blob, so files[0].text() is an asynchronous function that reads the content of the file into a string.
You may want to add the draggable option
Firs create your HTML
<div class="drag" id="drag_area">
<input class="box_file disabled" type="file" name="files[]" id="file" data-multiple-caption="{count} files selected" multiple />
<label for="file"><strong>Choose save file</strong><span class="box__dragndrop"> or drag it here</span>.</label>
</div>
Than write out your JS
$("#drag_area").on('drag dragstart dragend dragover dragenter dragleave drop', function (e) {
e.preventDefault();
e.stopPropagation();
})
.on('dragover dragenter', function () {
$("#drag_area").addClass('dr_active');
// this is needed if you wish to style your drag area on drag events
})
.on('dragleave dragend drop', function () {
$("#drag_area").removeClass('dr_active');
// this is needed if you wish to style your drag area on drag events
})
.on('drop', function (e) {
let droppedFiles = e.originalEvent.dataTransfer.files;
let reader = new FileReader()
reader.readAsDataURL(droppedFiles[0])
reader.onloadend = function () {
$.ajax({
url: reader.result,
success: function (data) {
console.log(JSON.parse(data)); // This is your JSON
},
error: function (request, error) {
cliLog(2, "Upload", "Cant upload save file")
}
});
}
}),
I'd made a more general approach with support to customize upload button title and callback when load is done:
function uploadJson(id, callback) {
document.getElementById(id).onchange = function(evt) {
try {
let files = evt.target.files;
if (!files.length) {
alert('No file selected!');
return;
}
let file = files[0];
let reader = new FileReader();
const self = this;
reader.onload = (event) => {
callback(event.target.result);
};
reader.readAsText(file);
} catch (err) {
console.error(err);
}
}
}
uploadJson('importJson', function (json) {
console.log(json);
});
<button onclick="document.getElementById('importJson').click()">import json</button>
<input id="importJson" value="import json" type="file" accept="application/json" style="display:none" />

Categories

Resources