In my app, I have an Upload button that enables the user to search files and upload them. I also defined a textarea and I want the user to be able to paste the file that he copied and upload it that way (like you would do in an e-mail or whatsapp etc.). Right now, when I try to paste the file that I've copied, nothing happens and the file doesn't paste. My code is below. What should I do to upload files by paste?
HTML:
<div>
<button mat-stroked-button class="primary-fg" (click)="onFileUploadClick($event)">
<mat-icon>attach_file</mat-icon>
Upload
</button>
<mat-form-field appearance="fill">
<mat-label>Paste</mat-label>
<textarea matInput
cdkTextareaAutosize
#autosize="cdkTextareaAutosize"
cdkAutosizeMinRows="1"
cdkAutosizeMaxRows="5"
id="pasteArea"></textarea>
</mat-form-field>
<script>
window.addEventListener("paste", e => {
if (e.clipboardData.files.length > 0) {
const fileInput = document.querySelector("#fileInput");
fileInput.files = e.clipboardData.files;
}
});
</script>
</div>
<input hidden type="file" id="fileInput" #fileInput multiple />
TS:
onFileUploadClick(event: any) {
event.preventDefault();
const fileInput = document.getElementById('fileInput') as HTMLInputElement;
fileInput.value = '';
fileInput.onchange = () => {
let tempFiles: IAttachment[] = [];
for (let index = 0; index < fileInput.files.length; index++) {
const file = fileInput.files[index];
const fileSize = file.size / 1024 / 1024; // in MB
if (fileSize <= 5) {
tempFiles.push({ FileInfo: file });
}
else {
this._dialog.open(FuseSimpleDialogComponent, {
width: "400px",
data: {
title: "Uyarı",
message: "Dosya boyutu 5MB'den büyük olduğundan seçilemez. Dosya Adı: " + file.name
}
});
}
}
tempFiles.forEach(f => this.uploadFile(f));
};
fileInput.click();
}
/**
*
* #param file
*/
private uploadFile(file: IAttachment) {
if (file.FileData) return; //file already uploaded
let fileReader: FileReader = new FileReader();
let $that = this;
let data: any;
fileReader.onerror = function (): void {
//show error message
};
fileReader.onloadend = function (): void {
if (FileReader.prototype.readAsBinaryString) {
data = btoa(fileReader.result as string);
}
else { //support for IE
data = ($that as any).arrayBufferToBase64(fileReader.result);
}
file.Name = file.FileInfo.name;
file.FileData = data;
file.CreateDate = new Date();
file.CreateUser = $that.user;
let allFiles = $that.Attachments ? $that.Attachments.slice(0) : [];
allFiles.unshift(file);
$that.Attachments = allFiles;
$that.AttachmentsChange.emit(allFiles);
};
if (FileReader.prototype.readAsBinaryString) {
fileReader.readAsBinaryString(file.FileInfo);
}
else { //support for IE
fileReader.readAsArrayBuffer(file.FileInfo);
}
}
Careful with you approach, simply pasting a file into a textarea will do nothing as a text area is made to receive... text.
You need to have an input file somewhere on your component and an event listener on window for the paste event.
Then take the paste event and try to see if it includes a file, if yes, start the uploading process, if not, paste the text in the textarea.
This is how I would approach it.
Related
I am following this documents https://www.webtricks.cf/2022/08/preview-an-image-before-it-is-uploaded.html for multiple image uploader and i am trying to delete one specific image or file from input mulitple file but unfortunatly preview image is deleting on client side but not deleting from input file please help me how can I resolve that thank u ?
html view
<input type="file" multiple />
javascript
/** Variables */
let files = [],
dragArea = document.querySelector('.drag-area'),
input = document.querySelector('.drag-area input'),
button = document.querySelector('.card button');
select = document.querySelector('.drag-area .select');
container = document.querySelector('.container');
/** CLICK LISTENER */
select.addEventListener('click', () => input.click());;
/* INPUT CHANGE EVENT */
input.addEventListener('change', () => {
let file = input.files;
// if user select more than 1 image or no image
if (file.length > 1 || file.length == 0) return;
files.push(file[0]);
input.files = null;
showImages();
})
/* SHOW IMAGES */
function showImages() {
let images = files.reduce(function(prev, file, index) {
return (prev += `<div class="image">
<img src="${URL.createObjectURL(file)}" alt="image">
<span onclick="delImage(${index})">×</span>
</div>`);
container.innerHTML = images;
}, "");
}
input.addEventListener('change', () => {
let file = input.files;
// if user select more than 1 image or no image
if (file.length > 1 || file.length == 0) return;
files.push(file[0]);
input.files = null;
container.innerHTML = `<div class="image">
<img src="${URL.createObjectURL(file[0])}" alt="image">
<span onclick="delImage(0)">×</span>
</div>`;
})
/* DELETE IMAGE */
function delImage(index) {
files.splice(index, 1);
showImages();
}
I am using a script based on this one to allow users to add multiple images to a form.
The form has multiple other inputs and the multi-image selection is part of it.
If I select files 'manually' using the 'click here' then the files are sent with the form submit. If the user drops them onto the 'dropzone' however, they do not get sent with the form. How can I make sure they do?
let dropBox = document.getElementById('dropBox');
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
dropBox.addEventListener(evt, prevDefault, false);
});
function prevDefault(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(evt => {
dropBox.addEventListener(evt, hover, false);
});
['dragleave', 'drop'].forEach(evt => {
dropBox.addEventListener(evt, unhover, false);
});
function hover(e) {
dropBox.classList.add('!bg-lime-100', 'border-lime-300');
}
function unhover(e) {
dropBox.classList.remove('!bg-lime-100', 'border-lime-300');
}
dropBox.addEventListener('drop', mngDrop, false);
function mngDrop(e) {
let dataTrans = e.dataTransfer;
let files = dataTrans.files;
filesManager(files);
}
function filesManager(files) {
files = [...files];
files.forEach(previewFile);
}
function previewFile(file) {
if (!file.type.match(/image.*/)) {
return alert('Only JPG, JPEG and PNG files are allowed');
}
let fReader = new FileReader();
let gallery = document.getElementById('gallery');
fReader.readAsDataURL(file);
fReader.onloadend = function() {
let wrap = document.createElement('div');
let img = document.createElement('img');
img.src = fReader.result;
img.classList.add('rounded-lg');
let imgCapt = document.createElement('p');
let fSize = Math.round((file.size / 1024 / 1024) * 10) / 10 + ' MB';
imgCapt.innerHTML = `<span class="text-md font-bold tracking-tight">${fSize}</span>`;
gallery.appendChild(wrap).appendChild(img);
gallery.appendChild(wrap).appendChild(imgCapt);
}
}
<form method="post" action="..." enctype="multipart/form-data">
<input type="text" name="name">
<input type="text" name="example">
<input type="text" name="etc">
<div id="dropBox" class="border-2 border-dashed bg-white border-slate-300 rounded-lg md:p-5 p-10 text-center">
<p class="mb-2 mt-4">Drag and drop your images here, or</p>
<input type="file" name="photos[]" id="imgUpload" multiple accept="image/*" onchange="filesManager(this.files)" class="hidden">
<label for="imgUpload" class="block mt-4 font-bold cursor-pointer"><u>Click here</u> to select from your computer</label>
<div id="gallery" class="mt-4 grid grid-cols-2 md:grid-cols-3 gap-4"></div>
</div>
</form>
I tried adding this in the previewFile function but that doesn't work:
let dropBox = document.getElementById('dropBox');
let formData = new FormData(dropBox[0]);
formData.append('photos', file);
<input type="file"> elements are read-only inputs. Files can't be assigned to them. For security reasons user input is necessary to attach a file on the client computer to a form. The "Browse..." or "Choose file..." button provides that user input.
In the case of a drag and drop interaction create a FormData object:
const photoFormData = new FormData();
...and append() each file to it using forEach():
files.forEach((file, idx) => {
photoFormData.append(`photos[${idx}]`, file);
});
The index (idx) of the file in the files array is used to differentiate each file in the FormData object.
When the user submits the form, use a submit addEventListener() to post the FormData object holding the files to the location the form is posted to using fetch():
document.querySelector('form').addEventListener('submit', function ({target}) {
fetch(target.action, {
method: 'put',
body: photoFormData
})
.then(result => { console.log('Success:', result); })
.catch(error => { console.error('Error:', error); });
});
Appending the files can happen in the filesManager() function:
function filesManager(files) {
files = [...files];
files.forEach((file, idx) => {
photoFormData.append(`photos[${idx}]`, file);
});
files.forEach(previewFile);
}
let dropBox = document.getElementById('dropBox');
const photoFormData = new FormData();
document.querySelector('form').addEventListener('submit', function ({target}) {
fetch(target.action, {
method: 'put',
body: photoFormData
})
.then(result => { console.log('Success:', result); })
.catch(error => { console.error('Error:', error); });
});
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(evt => {
dropBox.addEventListener(evt, prevDefault, false);
});
function prevDefault(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(evt => {
dropBox.addEventListener(evt, hover, false);
});
['dragleave', 'drop'].forEach(evt => {
dropBox.addEventListener(evt, unhover, false);
});
function hover(e) {
dropBox.classList.add('!bg-lime-100', 'border-lime-300');
}
function unhover(e) {
dropBox.classList.remove('!bg-lime-100', 'border-lime-300');
}
dropBox.addEventListener('drop', mngDrop, false);
function mngDrop(e) {
let dataTrans = e.dataTransfer;
let files = dataTrans.files;
filesManager(files);
}
function filesManager(files) {
files = [...files];
files.forEach((file, idx) => {
photoFormData.append(`photos[${idx}]`, file);
});
files.forEach(previewFile);
}
function previewFile(file) {
if (!file.type.match(/image.*/)) {
return alert('Only JPG, JPEG and PNG files are allowed');
}
let fReader = new FileReader();
let gallery = document.getElementById('gallery');
fReader.readAsDataURL(file);
fReader.onloadend = function() {
let wrap = document.createElement('div');
let img = document.createElement('img');
img.src = fReader.result;
img.classList.add('rounded-lg');
let imgCapt = document.createElement('p');
let fSize = Math.round((file.size / 1024 / 1024) * 10) / 10 + ' MB';
imgCapt.innerHTML = `<span class="text-md font-bold tracking-tight">${fSize}</span>`;
gallery.appendChild(wrap).appendChild(img);
gallery.appendChild(wrap).appendChild(imgCapt);
}
}
In the code snippet below, I would like to get rid of the 'Upload' button and wanna invoke this 'Upload()' inside 'incomingfile()' function. Is there a way to invoke the "Upload()" function without clicking the button?
<input type="file" id="file-upload" (change)="incomingfile($event)"/>
<label for="file-upload">Upload file</label>
<button type="button" class="btn btn-info" (click)="Upload()">Upload</button>
incomingfile(event) {
this.file = event.target.files[0]
}
Upload() {
let fileReader = new FileReader()
fileReader.onload = e => {
this.arrayBuffer = fileReader.result
var data = new Uint8Array(this.arrayBuffer)
var arr = new Array()
for (var i = 0; i != data.length; ++i)
arr[i] = String.fromCharCode(data[i])
var bstr = arr.join('')
var workbook = XLSX.read(bstr, { type: 'binary' })
var first_sheet_name = workbook.SheetNames[0]
var worksheet = workbook.Sheets[first_sheet_name]
this.fileData = XLSX.utils.sheet_to_json(worksheet, { raw: false })
console.log(this.fileData)
}
fileReader.readAsArrayBuffer(this.file)
}
You will have to trigger the DOM Button element in the function incomingfile:
const el = document.getElementsByTagName('button');
el.click();
Unless I'm missing something from your description, think you just need to call Upload() function instead of having a user event trigger it, so just add it to your other function:
incomingfile(event) {
this.file = event.target.files[0];
Upload();
}
Why don't u call like below code
incomingfile(event) {
this.file = event.target.files[0];
this.Upload();
}
Yes called upload method into incomingFile method like shown below.
incomingfile(event) {
this.file = event.target.files[0]
upload();
}
thanks
I'm trying to upload a file with the format of .csv/.xls/.xlsx and then read the file contents.
For example the following file
would output:
name,age,key
Mark,25,1
Jones,30,2
This is what I've implemented so far using react-file-reader and base-62 but it only works for the .csv files:
onFileUpload(file) {
var decodedData = base64.decode(file.base64);
}
<ReactFileReader fileTypes={[".csv",".xls", ".xlsx"]} base64={true} multipleFiles={false} handleFiles={this.onFileUpload}>
<button className='btn'>Upload</button>
</ReactFileReader>
Is there any way I can get the content of .xls and .xlsx files using the same way as I did for .csv files? Or maybe another module that does this...
I added the code as bellow,
Select Excel File
<ReactFileReader handleFiles={this.handleFiles} fileTypes={[".xls", ".xlsx", ".csv"]} base64={true}>
<button className='btn btn-warning btn-sm'>Select File</button>
{ this.state.isFileLoaded ? <label>File Loaded</label>
: <label>File Not Selected</label> }
</ReactFileReader>
<span id="errprojectLogoUrl" className="text text-danger"></span>
then I wrote the handleFiles as following and it was successful for me,
handleFiles = files => {
this.showLoading();
const currentMsgModal = {
...this.state.messageModal
};
currentMsgModal["isModalHidden"] = true;
let jsonBase64 = files.base64;
let index = jsonBase64.indexOf(',');
let encodedString = jsonBase64.substr(index + 1);
let fileName = files.fileList[0].name;
let formatString = /[^.]*$/.exec(fileName)[0];
let isCSVFile = formatString.includes('csv');
let isXLSFile = formatString.includes('xls');
let isXLSXFile = formatString.includes('xlsx');
if (isCSVFile || isXLSFile || isXLSXFile)
{
this.setState({
inputFileBaseString: encodedString,
isFileLoaded: true,
messageModal: currentMsgModal
});
}
else
{
currentMsgModal["messageType"] = "Error"
currentMsgModal["messageDescription"] = "File Type Not Supported"
currentMsgModal["isModalHidden"] = false;
this.setState({
messageModal: currentMsgModal
});
}
this.hideLoading();
}
I want to change name's file selected by input type=file, but it doesn't work.
This is my code:
$("document").ready(function() {
$('body').on('change', '#FileID', function() {
var name = document.getElementById('FileID');
name.files.item(0).name = Math.floor(Math.random() * 100000);
console.log('Selected file: ' + name.files.item(0).name);
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='FileID' class='class1' type='file' name='Images' value='' />
To change the name of a File object, you need to create a new File instance.
You can create one from a previous File object and it will act a simple wrapper over the data on disk.
The sketchier part is to update the <input type="file"> value to use this new File.
It is now possible through an hack as exposed in this answer of mine:
$('body').on('change', '#FileID', function(evt) {
const newName = Math.floor(Math.random() * 100000);
const input = evt.currentTarget;
const previousFile = input.files[0];
const newFile = new File([previousFile], newName);
// hack to update the selected file
const dT = new DataTransfer();
dT.items.add(newFile);
input.files = dT.files;
console.log('Selected file: ' + input.files.item(0).name);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input id='FileID' class='class1' type='file' name='Images' value='' />
However since this part is still mainly an hack, I can't really recommend its use.
So if you don't need to show the updated value in the native input, don't do it. Simply update a custom UI, and use a FormData to upload to your server. The last param of FormData#append(blob, fieldname, filename) will set the filename sent by the browser in the form-data request:
const form = new FormData();
const file = new File(["some data"], "originalname.txt");
form.append("fileField", file, Math.floor(Math.random() * 100000));
console.log("original file's name: ", file.name);
new Response(form).text()
.then((content) => console.log("formdata content: ", content));
So you should not need the aforementioned hacks at all.
For anyone ending here trying to get rid of the file's name, try converting it to base64. this way it won't have the name attached to it and you could upload it how you want. I will leave a code example using reactJS for this.
1: Here is the input file type calling the onChange event with our function:
<input onChange={this.handleBackgroundImagePreview} type="file" accept="image/png,image/gif,image/jpeg"></input>
2: Then convert that image to base64
handleBackgroundImagePreview = (e) =>{
// Retrieves the selected Image or file
const file = e.target.files[0]
//Calling async file reader with a callback
let fileBase64 = '';
this.getBase64(file, (result) => {
fileBase64 = result;
console.log(fileBase64)
//In here you could upload to the server the base 64 data as you want
});
}
getBase64(file, cb) {
let reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
cb(reader.result)
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}