Preventing duplicate files in an input file - javascript

I'm using loodash cloneDeep to upload files, however I need files to not be duplicated and only be able to upload a file once. How can I do this using cloneDeep?
I don't know how to do it, I googled, but the solution was only for jquery
const [files, setFiles] = 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
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);
}
})
);
}
Promise.all(promises).then(r => {
setFiles(fileArr);
})
}
catch(e) {
console.log(e);
}
}

If relying on the filenames is enough, you can try to store them to check if it has been uploaded already :
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);
}
}
Note: this will not prevent the case when the user upload a file, then refresh the website and upload the same file again
If you want to prevent that you have to ask your backend if the file has already been upload or not.

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));
}

How to upload a blob into firebase Storage?

i'm using next js 13 with firebase v9. and i'm using a drop zone to upload images. the dropzone returns an array with blob as it's src.
[
{
id: 1
name: "image_processing20220628-4591-yzir35.png"
src: "blob:http://localhost:3000/6e2f33e5-a749-4e9a-b502-d20b8e3f38ca"
}
...
]
the above array is returned from the drop zone. and when i tried to upload to firebase storage it throws an error .
FirebaseError: Firebase Storage: Object 'livingImages/blob:http:/localhost:3000/ca0e3eaf-dbe9-4d77-8053-f4b6d1bd8600' does not exist. (storage/object-not-found)
so how can i upload blob of images to firebase storage?
const imgURL = [];
//this is the images stored inside Redux
const images = useSelector(selectImages);
const storage = getStorage();
images.map(async (file) => {
const storageRef = ref(storage, `livingImages/${file.src}`);
await getDownloadURL(storageRef).then((url) => {
imgURL.push(url);
});
});
const createDocument = () => {
const docRef = doc(db, "livingPosts", session?.user?.email);
const colRef = collection(docRef, "posts");
addDoc(colRef, {
name: "test upload",
images: imgURL,
});
};
the dropzone code
const dispatch = useDispatch();
const images = useSelector(selectImages);
const [files, setFiles] = useState(images == [] ? [] : images);
const {getRootProps, getInputProps} = useDropzone({
onDrop: (acceptedFiles) => {
acceptedFiles.map((file, index) => {
const reader = new FileReader();
reader.onload = async function (e) {
const options = {
maxSizeMB: 5,
maxWidthOrHeight: 1920,
useWebWorker: true,
};
const compressedFile = await imageCompression(file, options);
const tot = parseInt(acceptedFiles.length) + parseInt(files.length);
if (tot > 9) {
alert("select maximum of 9 images");
} else if (parseInt(acceptedFiles.length) > 9) {
alert("maximum images to be selected is 9");
} else if (parseInt(files.length) < 9) {
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
name: file.name,
},
]);
files.map((filename) => {
acceptedFiles.forEach((newFile) => {
if (newFile.name == filename.name) {
alert("a duplicate image is detected");
setFiles(
files,
files.filter((val) => val !== newFile)
);
}
});
});
} else {
alert("something went wrong");
}
};
reader.readAsDataURL(file);
return file;
});
},
})
and the output of the dropzone is
As mentioned in the comments, you'll need the actual File or Blob object to upload the file and not the object URL. You can set the blob in state as shown below:
setFiles((prevState) => [
...prevState,
{
id: index,
src: URL.createObjectURL(compressedFile),
blob: compressedFile, // <-- add blob
name: file.name,
},
]);
Then to upload the files and storing download URLs in Firestore document, try the following function:
import { ref, uploadBytes, getDownloadURL } from "firebase/storage";
import { addDoc } from "firebase/firestore";
const uploadFiles = async () => {
console.log(files);
const promises = files.map((file) => {
const storageRef = ref(storage, `images/${file.name}`);
return uploadBytes(storageRef, file.blob);
});
// upload all files
const res = await Promise.all(promises);
// get download URLs
const links = await Promise.all(res.map((r) => getDownloadURL(r.ref)));
console.log({ links })
// Add Firestore document
const colRef = collection(db, "livingPosts", session?.user?.email, "posts")
const docRef = await addDoc(colRef, {
name: "test",
images: links,
});
console.log("Document written with ID: ", docRef.id);
};
You can call this function on a submit button click or any event when you want to start the upload.

How to use jquery get JSON from the backend and update innerHTML on the frontend

This is in my index.ejs
<script src="https://cdn.jsdelivr.net/npm/ipfs-core/dist/index.min.js"></script>
<script>
async function saveFile(file) {
const node = window.IpfsCore.create();
return await node.files.add(file);
}
function receivedText() {
document.getElementById('ipfs').appendChild(document.createTextNode(fr.result));
}
async function readfichier(e) {
if(window.FileReader) {
var file = e.target.files[0];
var reader = new FileReader();
if (file && file.type.match('image.*')) {
reader.readAsDataURL(file);
}
reader.onloadend = function (e) {
}
console.log(file);
document.getElementById('fileNameOutput').innerHTML = document.getElementById('fileName').value;
document.getElementById('ipfs').innerHTML = await saveFile(JSON.stringify(file));
}
}
function clickme() {
$('file').ready(function(){ const file = $('file').prop('files')});
var fr = new FileReader();
fr.onload = select;
document.getElementById('file').addEventListener('change', readfichier, false);
//fr.readAsText(file);
//fr.readAsBinaryString(file); //as bit work with base64 for example upload to server
}
function select() {
var id = $('file').prop('files');
$.getJSON("/upload?" + $.param(id), function (res) {
document.getElementById('ipfs').innerHTML = res;
});
}
</script>
And this is in my index.js
app.get('/upload', async (req,res) => {
const file = req.files.file;
const fileHash = await saveFile(JSON.stringify(file));
res.send(fileHash)
})
function success(result){
return result
}
async function saveFile(file) {
await ipfs.add(file, (err, result) => {
if (err){
console.log(err);
}
console.log(result);
});
}
I'm trying to upload a file and store it on the IPFS blockchain, and then load the hash of that file into the front-end.
What this should enable me to do is to load PDFs and Videos and Music and Images, and store them all on the blockchain and then implement them all onsite.

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