Removing large files from file upload - javascript

I would like to allow a user to upload multiple files through Ajax. If some of the files are too large I would like to remove them from the upload list. How can I go about doing this? I currently have the following for my input field.
<input id="file" type="file" onChange={this.handleChange.bind(this)} required multiple />
So far I have come up with something like this to verify which files are too large:
handleChange(event) {
const target = event.target;
if (target.type === 'file'){
console.log("FILES")
const files = target.files
console.log("The following files are too large")
for (let i=0; i < files.length; i++){
if (files[i].size > 50000){
console.log(files[i].name)
}
}
}
}
The thing I am confused about is how files are handled, as they are not really links to the actual files on the file system. How does the file structure work? How can I store the good files into a new file structure that is uploadable with Ajax?

target.files is just an array. You can simple filter it, removing the files you don't want and do whatever you want with the new array (e.g. store it in the component's state).
const files = target.files.filter(file => file.size <= 50000);
this.setState({files});
// or uploadFiles(files)
If you are asking how to actually send files via an XMLHTTPRequest, this can be done with FormData:
const data = new FormData();
files.forEach(file => data.append(file.name, file));
and then send the FormData object via the XMLHTTPRequest.

Related

Is it possible to upload a JSON file though file upload and read the contents in Angular 6 without talking to an API?

Have a downloaded JSON file as a structure. Want to be able to upload it though file upload in Angular (Using Angular 6) and then read the contents of the file directly in Angular, rather than uploading it to an API.
Searching for similar seams to bring back how to read a local file on a server in Angular, rather than through file upload.
You can try the following:
//In your Template:
<input type="file" name="files" (change)="uploadFile($event)" />
//In your component:
uploadFile(event) {
if (event.target.files.length !== 1) {
console.error('No file selected');
} else {
const reader = new FileReader();
reader.onloadend = (e) => {
// handle data processing
console.log(reader.result.toString());
};
reader.readAsText(event.target.files[0]);
}
}
I created this demo. Have a look and check if that is what you need.

Edit a file to be uploaded

I would like to be able to edit a file that has been selected for upload. I want to search and replace text in case absolute files should be made relative...
I notice in the File API I can do some of it, but I get a little stuck:
document.getElementById('exampleInputFile').onchange = function(event) {
var fileToLoad = event.target.files[0];
if (fileToLoad) {
var reader = new FileReader();
reader.onload = function(fileLoadedEvent) {
var textFromFileLoaded = fileLoadedEvent.target.result;
//Use logic to remove absolute files
//Upload S3
};
reader.readAsText(fileToLoad, 'UTF-8');
}
};
I am trying to figure out how now to convert that text to a proper File so that I can upload it to S3 using an existing api that expects something returned by: event.target.files[0] code above.
I do not want the server to handle any heavy lifting here if I can avoid it (files can easily be a few megabytes since they can be 3D models).
Assuming you know the url of the file when it lands in the S3 bucket, you can retrieve the file using a http.get, which will give you the contents of the (I assume plain text file). You can then parse that file and do whatever modification you need to do on the contents. If the file has changed, you can then write it back to the S3 bucket to replace the original file.
On AWS you can use Lambda to execute NodeJS code when an event is triggered (for example an upload to a specified bucket).

Upload and read file client side, with angular 2

I need log file(s) from user so I can read and analyze those. For example somekind of drop area, where user drops a file, then I can read it with javascript?
I use Angular2 rc5. I have node.js running backside, but I don't need the data there. I need it only at client side.
Is it possible to read and parse file content with just frontend tech, like angular2 and javascript? Or do I have to upload the file to server and analyze it there?
It is possible!
I ended up doing it like this. This reads all the files that are selected with file dialog. I don't need to send these to node.js. I can just manipulate these on client.
<input type='file' accept='text/plain' multiple (change)='openFile($event)'>
openFile(event) {
let input = event.target;
for (var index = 0; index < input.files.length; index++) {
let reader = new FileReader();
reader.onload = () => {
// this 'text' is the content of the file
var text = reader.result;
}
reader.readAsText(input.files[index]);
};
}
It is very basic example of how you can do it.

Using req.files for multiple uploads

So i am handling a multiple file upload on my client side, where my html looks like the following
form(method='post', enctype='multipart/form-data')#createReportForm
input(type='file', multiple='multiple', accept='image/png, image/gif, image/jpeg, image/jpg', name='uploadImages', data-max-size='5000000')#uploadFile
Now on my server side to access the contents of the file and other info i am using
req.files.uploadImages. This works fine if one file attached, but when multiple files are attached on the client this object only reads the last attached file and not the first one
Whats the reasoning behind this? Shouldn't req.files.uploadImages have info about both the files?
If multiple files are selected the req.files.uploadImages would hold all the files.
You can just loop over them:
var files = [].concat(req.files.uploadImages);
for(var x = 0; x < files.length; x++){
// upload file
}

How to remove one specific selected file from input file control

How to remove one specific selected file from input file control?
I have an input file control with the option to select multiple files; however, I want to validate a file and if it has an wrong extension then I should remove that file from the file control itself, is it possible?
I tried as below
<input type="file" name="fileToUpload" id="fileToUpload" multiple/>
<script> $("#fileToUpload")[0].files[0] </script>
Below is the screenshot of the object but I am not able to modify it
As other people pointed out, FileList is read only. You can get around this by pushing those files into a separate Array though. You can then do whatever you want with that curated list of files. If uploading them to a server is the goal, you can use the FileReader API.
Below is a round about way of completely avoiding needing to modify the FileList.
Steps:
Add normal file input change event listener
Loop through each file from change event, filter for desired validation
Push valid files into separate array
Use FileReader API to read files locally
Submit valid, processed files to server
Event handler and basic file loop code:
var validatedFiles = [];
$("#fileToUpload").on("change", function (event) {
var files = event.originalEvent.target.files;
files.forEach(function (file) {
if (file.name.matches(/something.txt/)) {
validatedFiles.push(file); // Simplest case
} else {
/* do something else */
}
});
});
Below is a more complicated version of the file loop that shows how you can use the FileReader API to load the file into the browser and optionally submit it to a server as a Base64 encoded blob.
files.forEach(function (file) {
if (file.name.matches(/something.txt/)) { // You could also do more complicated validation after processing the file client side
var reader = new FileReader();
// Setup listener
reader.onload = (function (processedFile) {
return function (e) {
var fileData = { name : processedFile.name, fileData : e.target.result };
// Submit individual file to server
$.post("/your/url/here", fileData);
// or add to list to submit as group later
validatedFiles.push(fileData);
};
})(file);
// Process file
reader.readAsDataURL(file);
} else {
/* still do something else */
}
});
A note of caution about using FileReader API. Base64 encoding a file will increase its size by around 30%. If that isn't acceptable you will need to try something else.
I know this is an old post but I have spent ages trying to work around this one so I'll post my solution. There is a way to update the fileList of an fileField element with another fileList - which can be done with DataTransfer:
let updateFileList = function (fileField, index) {
let fileBuffer = Array.from(fileField.files);
fileBuffer.splice(index, 1);
/** Code from: https://stackoverflow.com/a/47172409/8145428 */
const dT = new ClipboardEvent('').clipboardData || // Firefox < 62 workaround exploiting https://bugzilla.mozilla.org/show_bug.cgi?id=1422655
new DataTransfer(); // specs compliant (as of March 2018 only Chrome)
for (let file of fileBuffer) { dT.items.add(file); }
fileField.files = dT.files;
}
The above function takes the fileField as a DOM Object and the index of the file in the fileField's fileList to be removed and updates the passed fileField accordingly.
Hope this saves somebody else some time!
How to set File objects and length property at FileList object where the files are also reflected at FormData object?
I thought that I should add my comment here as well here (I've answered here: JavaScript delete File from FileList to be uploaded)
I found a workaround. This will not require AJAX for the request at all and the form can be sent to the server. Basically you could create an hidden or text input and set it's value attribute to the base64 string created after processing the file selected.
<input type=hidden value=${base64string} />
You will probably consider the idea to create multiple input file instead of input text or hidden. This will not work as we can't assign a value to it.
This method will include the input file in the data sent to the database and to ignore the input file you could:
in the back-end don't consider the field;
you can set the disabled attribute to the input file before serialising the form;
remove the DOM element before sending data.
When you want to delete a file just get the index of the element and remove the input element (text or hidden) from the DOM.
Requirements:
You need to write the logic to convert files in base64 and store all files inside an array whenever the input file trigger the change event.
Pros:
This will basically give you a lot of control and you can filter, comparing files, check for file size, MIME type, and so on..
Based on #liamthorne4 answer, here is a working solution for upload, list and delete element from list, tested on Firefox and Chrome:
HTML:
<button onclick="uploadFile(event)">Upload files</button>
<div id="listfiles" class="view_list"></div>
<input class="hidden" type="file" id="input_file_id" onchange="fileList(event)" name="files[]" multiple>
JS:
function uploadFile(evt) {
evt.preventDefault();
$('#input_file_id').click();
}
function fileList(e) {
var files = $('#input_file_id').prop("files");
var names = $.map(files, function(val) { return val.name; });
for (n in names) {
$("#listfiles").append("<div id=preload_"+n+" title='"+names[n]+"'><p>"+names[n]+"</p><a onclick=deleteFile("+n+")>Delete</a></div>");
}
}
function deleteFile(index) {
filelistall = $('#input_file_id').prop("files");
var fileBuffer=[];
Array.prototype.push.apply( fileBuffer, filelistall );
fileBuffer.splice(index, 1);
const dT = new ClipboardEvent('').clipboardData || new DataTransfer();
for (let file of fileBuffer) { dT.items.add(file); }
filelistall = $('#input_file_id').prop("files",dT.files);
$("#preload_"+index).remove()
}
html
<input id="fileInput" name="fileInput" type="file" />
<input onclick="clearFileInput()" type="button" value="Clear" />
javascript
function clearFileInput(){
var oldInput = document.getElementById("fileInput");
var newInput = document.createElement("input");
newInput.type = "file";
newInput.id = oldInput.id;
newInput.name = oldInput.name;
newInput.className = oldInput.className;
newInput.style.cssText = oldInput.style.cssText;
// copy any other relevant attributes
oldInput.parentNode.replaceChild(newInput, oldInput);
}

Categories

Resources