Convert multiple images to base 64 using single function - javascript

I'm working on a problem where I have to take three images as input from the user and have to send them to the backend by converting them into Base64. I know how to do it for a single input file but can't work my way around for multiple inputs.
I want to have a single function that can convert the images to Base64 & store the value of each image in a separate variable. Please help me out with this.
Following is the code I'm using for single input i.e. First Image.
HTML CODE
<div class="first_div">
<label for="first_image">First Image</label>
<input name="first_image" type="file" accept="image/*" id="first_image" class="img_file">
</div>
<div class="second_div">
<label for="second_image">Second Image</label>
<input name="second_image" type="file" accept="image/*" id="second_image" class="img_file">
</div>
<div class="third_div">
<label for="third_image">Third Image</label>
<input name="third_image" type="file" accept="image/*" id="third_image" class="img_file">
</div>
<button onclick="submitImages()">Submit</button>
JAVASCRIPT CODE
let encoded_image;
function getBase64(file) {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function () {
console.log(reader.result);
encoded_image = reader.result;
};
reader.onerror = function (error) {
console.log('Error: ', error);
};
}
const submitImages = () => {
var files = document.getElementById('first_image').files[0];
if (files.length > 0) {
getBase64(files[0]);
}
const formData = new URLSearchParams(new FormData());
formData.append("first_image", encoded_image);
fetch(API CALL HERE)
}
I want to create a function that takes input from all three fields, converts them to Base64 & stores in a variable. So that I can append it to form data.

Select all inputs, loop and get base64 of each file
Try this
const getBase64 = (file) =>
new Promise((resolve, reject) => {
console.log(file);
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = () => resolve(reader.result);
reader.onerror = (err) => reject(err);
});
const submitImages = async () => {
const imageInputs = document.querySelectorAll("input");
const images = await Promise.all(
[...imageInputs].map((imageInput) =>
imageInput?.files?.[0] ? getBase64(imageInput.files[0]) : null
)
);
console.log(images);
};

This example is in React but you can easily modify it to standard javascript.
If it's standard javascript just change const [images, setImages] = useState([]); into let images = [].
const [images, setImages] = useState([]);
const formatImage = async() => {
try {
for (let i = 0; i < e.target.files.length; i++) {
const reader = new FileReader();
reader.readAsDataURL(e.target.files[i]);
reader.onload = (readerEvent) => {
images.push(readerEvent.target ? .result);
};
}
} catch (error) {
console.log(error);
}
};
<input type="file" multiple onChange={formatImage} />
And here is an example of putting these images on Firebase Firestore and again if it's standard javascript change setImages([]) to images = []
const addImage = async() => {
try {
Promise.all(
images.map(
async(file: any) =>
await addDoc(collection(db, "images"), {
image: file,
date: Timestamp.now(),
})
)
);
setImages([]);
} catch (error) {
console.log(error);
}
};

Related

How to add custom metadata in PDF file using react js?

I'm taking input as a PDF file and using javascript to add custom metadata, but I'm not getting a satisfactory result.
Below is a sample method code that I used to add custom metadata that is first converted to blob type and then added, but when we convert its blob data to base64 and download the file and check the properties, we cannot find it.
const blobToBase64 = (blob: any) =>
new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(blob);
reader.onload = () => resolve(reader.result);
reader.onerror = (error) => reject(error);
});
const updatePDFMetaData = (file: any, metadata: any) => {
let convertBlobToBase64: any;
const selectedFile = file;
const reader = new FileReader();
reader.readAsArrayBuffer(selectedFile);
reader.onload = async (event:any) => {
const fileBuffer: any = event?.target?.result;
const blob: any = new Blob([fileBuffer], { type: selectedFile.type });
Object.keys(metadata).forEach((key: any) => {
blob[key] = metadata[key];
});
convertBlobToBase64 = await blobToBase64(blob);
console.log("convertBlobToBase64", convertBlobToBase64);
};
};

Removing duplicate files upload-files

I use lodash clonedeep for uploading files.
I wrote a function that forbids uploading identical files. But if I delete some file after uploading, it still stays in state and I can't upload file with the same name.
What can I do to get the file removed from the state too?
const [files, setFiles] = useState([]);
//state to store uploaded file's name
const [fileNames, setFileNames] = useState([]);
const onSelectFile = (e) => {
try {
let fileArr = cloneDeep(files);
let promises = [];
for (let file of e.target.files) {
promises.push(
new Promise((resolve, reject) => {
const fileName = file.name
//if the file has not been already uploaded
if (!fileNames.includes(fileName)) {
//add the current fileName in state
setFileNames([fileName, ...fileNames]);
const type = file.type;
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function (evt) {
const fileData = evt.target.result;
fileArr.push({
name: fileName,
type: type,
data: fileData,
comment: "",
id: `${new Date().getTime()}_${fileName}`,
canDelete: true
});
if (typeof props.onFileSelected == "function")
props.onFileSelected(fileArr);
resolve(true);
}
reader.onerror = function (evt) {
console.log("error reading file");
reject(false);
}
} else {
alert("File has already been uploaded");
reject(false);
}
})
);
}
Promise.all(promises).then(r => {
setFiles(fileArr);
})
}
catch(e) {
console.log(e);
}
}
I don’t know what to do, it took me 40 hours to think, but I still didn’t understand anything.
From what I can understand from your question, you aren't saving the images on disk. You also haven't included the logic you are using for deleting files. Either way, the implementation is similar.
So when a user deletes a file, assuming they are deleting by filename, we use the filter() method to only keep those that aren't the file we want to delete.
const fileNameToRemove = 'example.txt';
setFiles(files.filter(file=> file.name !== fileNameToRemove));
setFileNames(fileNames.filter(name => name !== fileNameToRemove));
So you will want to do something like this, I haven't used clonedeep like you're in this example, but it's a quick add. I have also moved the read file section into its own function, and am I returning a promise, so I can use async/await within the core upload function.
const [files, setFiles] = useState([]);
const [fileNames, setFileNames] = useState([]);
const readFileAsync = async (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = () => {
resolve(fileReader.result);
}
fileReader.onerror = () => {
reject(null);
}
})
}
const onSelectFile = async (e) => {
const uploadingFiles = e.target.files;
for (const file of uploadingFiles) {
const fileName = file.name;
if (fileNames.includes(fileName)) {
console.error("File duplicate");
continue;
}
try {
const fileContentsBuffer = await readFileAsync(file);
setFiles([...files, {
name: fileName,
type: file.type,
data: fileContentsBuffer,
comment: "",
id: `${new Date().getTime()}_${fileName}`,
canDelete: true
}])
setFileNames([...fileNames, fileName]);
} catch (e) {
console.error("Error reading file");
continue;
}
}
}
const deleteFile = (fileName) => {
const fileDataToDelete = files.find((file) => {
return file.name === fileName;
})
if (!fileDataToDelete.canDelete) {
console.error('Can\'t delete file!');
return;
}
setFileNames(fileNames.filter(name => name != fileName));
setFiles(files.filter(file => file.name != fileName && file.canDelete));
}

converting image to base64 - image becomes invisible

I'm trying encode an image to base64, (so I can later send it this way to a backend server). Everything seems to work until I use JSON.stringify() on the object that has the encoded image in it.
I think It gets lost in the JSON.stringify() and I can't seem to find a solution. I've been working for weeks on this issue and I couldn't find an answer anywhere. Please help!
const [baseImage, setBaseImage] = useState('');
const [baseImageCorrect, setBaseImageCorrect] = useState('');
const convertBase64 = (file) => {
return new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsDataURL(file);
fileReader.onload = () => {
resolve(fileReader.result);
};
fileReader.onerror = (error) => {
reject(error);
console.log(error);
};
});
};
const uploadImage = async (e) => {
const file = e.target.files[0];
const base64 = await convertBase64(file);
const base64RemovedType = base64.split(',')[1];
setBaseImage(`${base64RemovedType}`);
};
useEffect(() => {
setBaseImageCorrect(baseImage);
console.log('current:' + baseImageCorrect);
//prints out a long string with the RIGHT information
}, [baseImage, baseImageCorrect]);
const EncodedImage = JSON.stringify({
fileBase64: (baseImageCorrect, { encoding: 'base64' }),
});
console.log(EncodedImage)
//PRINTS THIS: "fileBase64":{"encoding":"base64"}} , without the encoded image string
I am assuming u need the key baseImageCorrect and encoding key at the same level.
Use this instead:
const EncodedImage = JSON.stringify({
fileBase64: {baseImageCorrect, encoding: 'base64' },
});

Javascript react safe image in database

I have a problem with saving pictures in the database. I want to do a post Method, where i can safe a file in a directory and save the picture link in the database.
Here is my Code:
`const toBase64 = file => new Promise((resolve, reject) => {
const reader = new FileReader()
reader.readAsDataURL(file)
reader.onload = () => resolve(reader.result)
reader.onerror = error => reject(error)
})`
` const [base64Image, setBase64Image] = useState("")
const [imagePath, setImagePath] = useState("")
const fileInput = useRef(null)`
`const onFileInputChange = async (e) => {
const file = fileInput.current.files[0]
if (!file) return
const base64 = await toBase64(file)
setBase64Image(base64)}`
` const handleSubmitImage = async (e) => {
e.preventDefault()
if (!base64Image) return
const response = await fetch("/public", {
method: "POST",
headers: {
"content-type": "application/json"
},
body: JSON.stringify(base64Image)
})
const data = await response.json()
setImagePath(data.filePath)
}`
Post:
`const handleSubmit = async (e) => {
e.preventDefault()
setIsLoading(true)
setErrors(defaultModel)
const result = validateModel(post)
if (!result.isValid) {
setErrors(result.errors)
setIsLoading(false)
return
}
if (post.id) {
await updatePost(post, session.accessToken)
alert("Post updated!")
router.push(`/posts/${post.id}`)
} else {
const newPost = await createPost(post, session.accessToken)
alert("Post created!")
router.push(`/posts/${newPost.id}`)
}
setIsLoading(false)
}
`
` <fieldset onSubmit={handleSubmitImage} className={styles.form}>
<p>Image:</p>
<input value={post.image}
type="file"
accept=".png,.jpg"
ref={fileInput}
onChange={onFileInputChange}
/>
{/* eslint-disable-next-line #next/next/no-img-element */}
{base64Image && <img src={base64Image} style={{width: "1000px", height: "1000px"}} alt={""}/>}
{imagePath && <p>
<Link href={`http://localhost:3000${imagePath}`}
passHref><a>http://localhost:3000{imagePath}</a></Link>
</p>
}
</fieldset>`
Right now i can connect to the Explorer and pick an Image. I can also display the image. If i press on create, it doesnt work properly with saving the image in the database.

How can I read the data in the excel file with reactjs or javascript using the path to the file

I want to read the contents of the file directly by using the file path. I can do this by having the file selected. But I don't know how to do it using the direct file path. I could not find any examples or sources for this. Below is how I read the file by selecting it from the input.
import * as XLSX from 'xlsx';
var items = [];
readExcel = (file) => {
const promise = new Promise((resolve, reject) => {
const fileReader = new FileReader();
fileReader.readAsArrayBuffer(file);
fileReader.onload = (e) => {
const bufferArray = e.target.result;
const wb = XLSX.read(bufferArray, { type: "buffer" });
const wsname = wb.SheetNames[0];
const ws = wb.Sheets[wsname];
const data = XLSX.utils.sheet_to_json(ws);
resolve(data);
};
fileReader.onerror = (error) => {
reject(error);
};
});
promise.then((d) => {
this.items = d;
console.log(this.items)
// fill dictionary
this.dictionary = Object.assign({}, ...this.items.map((x) => ({ [x.PartNumber]: x.Cost })));
console.log(this.dictionary)
});
};
<input
type="file"
onChange={(e) => {
const file = e.target.files[0];
this.readExcel(file);
}}
/>
I beleive it should work:
const req = new XMLHttpRequest();
req.responseType = "arraybuffer";
req.open("GET", "https://.../MyExcelFile.xlsx", true);
req.onload = () => {
const bufferArray = req.response;
const wb = XLSX.read(bufferArray, { type: "buffer" });
...
I couldn't find a direct read operation. I converted the excel file to json format and got my job done.

Categories

Resources