I am trying to upload and save multiple JSON files. previously for uploading and saving a single JSON file I was doing this:
<input type="file" id="file" ref="fileSelect" @change="showfiles" />
showfiles() {
let files = (this.$refs.fileSelect as HTMLInputElement).files
var reader = new FileReader();
reader.onload = () => { this.data = JSON.parse(reader.result as string); }
reader.readAsText(files![0]);
}
I was saving the json file in this.data
Now I want to upload and save multiple JSON files similarly and save them in this.data
So I updated the above code as follows (I thought I can loop and append the file data):
<input multiple type="file" id="file" ref="fileSelect" @change="showfiles" />
showfiles() {
let files = (this.$refs.fileSelect as HTMLInputElement).files
for(let i=0;i<files!.length;i ){
var reader = new FileReader();
reader.onload = () => { this.data[i] = JSON.parse(reader.result as string); }
reader.readAsText(files![i]);}
}
But this is not working as intended, it is only appending the file selected last multiple times (and the 0th index is null, so for example if I select file1.json , file2.json, file3.json
in this.data
0 is null and 1,2 index are data from file3.json
.
What am I doing wrong?
My current complete code:
<input multiple type="file" id="file" ref="fileSelect" @change="showfiles" />
<script lang='ts'>
import { defineComponent } from "vue"
export default defineComponent({
name: 'HomeView',
props: [],
data(){
return{
data:{}
};
},
methods: {
showfiles() {
this.data={}
let files = (this.$refs.fileSelect as HTMLInputElement).files
for(let i=0;i<files!.length;i ){
var reader = new FileReader();
reader.onload = () => { this.data[i] = JSON.parse(reader.result as string);}
reader.readAsText(files![i]);
}
}
CodePudding user response:
There's really nothing Vue or TypeScript specific in your question, so I'll attempt to answer with plain old JavaScript. I've included a runnable code snippet.
In a nutshell, whenever the selected files change:
- Create a single
FileReader
instead of one per selected file. - Attach a single
load
event listener, and obtain theFileReader
'sresult
(the file content). - Read each file, one at a time, waiting for each file to be loaded and read prior to reading the next file.
Were it me, I would make a function to read a file and resolve with the file's content. You could then call that function from your showFiles
method, and await
the result of each file.
Here's a runnable example in raw JavaScript. Note that inputFile
corresponds to your fileSelect
reference, and the results
array corresponds to your data
array (which, BTW, should be an array not an object). Also, I've wired up the change
event listener in the JS code, but it's the same as the @change
listener in your template.
(function() {
'use strict'
const inputFile = document.getElementById('inputFile')
// Holds the contents of each selected file.
const results = []
inputFile.addEventListener('change', async e => {
const files = e.target.files
const reader = new FileReader()
// New set of results each time.
results.splice(0)
for (const file of files)
results.push(await readFile(reader, file))
// Do something with the files.
console.log(results)
})
/**
* Read a file asynchronously and resolve with the contents.
*
* @param {FileReader} reader - A FileReader instance that will read `file`
* as text.
* @param {File} file - A selected File instance.
*/
function readFile(reader, file) {
const deferredResult = new Promise(resolve => {
reader.addEventListener('load', function getResult() {
resolve(reader.result)
reader.removeEventListener('load', getResult)
})
})
reader.readAsText(file)
return deferredResult
}
})()
<!doctype html>
<html>
<head>
</head>
<body>
<input multiple type="file" id="inputFile" accept=".json">
</body>
</html>