I have rendered a PDF file in react. I want to add a signature image to it by choosing an image file from the system and place it wherever I place the cursor on a pdf file.
After adding the signature we have to download the pdf along with the signature image on it.
I have used the #react-pdf-viewer library.
I tried but I don't know how to exactly approach this problem.
import React, { useState } from "react";
// Import the main component
import { Viewer } from "#react-pdf-viewer/core"; // install this library
// Plugins
import { defaultLayoutPlugin } from "#react-pdf-viewer/default-layout"; // install this library
// Import the styles
import "#react-pdf-viewer/core/lib/styles/index.css";
import "#react-pdf-viewer/default-layout/lib/styles/index.css";
// Worker
import { Worker } from "#react-pdf-viewer/core"; // install this library
export const App = () => {
// Create new plugin instance
const defaultLayoutPluginInstance = defaultLayoutPlugin();
//for unchange event
const [pdfFile, setPdfFile] = useState(null);
const [pdfFileError, setPdfFileError] = useState("");
//for submit event
const [viewPdf, setViewPdf] = useState(null);
// onchange event
const fileType = ["application/pdf"];
const handlePdfFileChange = (e) => {
let selectedFile = e.target.files[0];
if (selectedFile) {
if (selectedFile && fileType.includes(selectedFile.type)) {
let reader = new FileReader();
reader.readAsDataURL(selectedFile);
reader.onloadend = (e) => {
setPdfFile(e.target.result);
setPdfFileError("");
};
} else {
setPdfFile(null);
setPdfFileError("Please select valid pdf file");
}
} else {
console.log("select your file");
}
};
// form submit
const handlePdfFileSubmit = (e) => {
e.preventDefault();
if (pdfFile !== null) {
setViewPdf(pdfFile);
} else {
setViewPdf(null);
}
};
return (
<div className="container">
<br></br>
<form className="form-group" onSubmit={handlePdfFileSubmit}>
<input
type="file"
className="form-control"
required
onChange={handlePdfFileChange}
/>
{pdfFileError && <div className="error-msg">{pdfFileError}</div>}
<br></br>
<button type="submit" classname="btn btn-success btn-lg">
UPLOAD
</button>
</form>
<br></br>
<h4>View PDF</h4>
<div className="pdf-container">
{/* show pdf */}
{/* show pdf conditionally (if we have one) */}
{viewPdf && (
<>
<Worker workerUrl="https://unpkg.com/pdfjs-dist#2.6.347/build/pdf.worker.min.js">
<Viewer
fileUrl={viewPdf}
plugins={[defaultLayoutPluginInstance]}
/>
</Worker>
</>
)}
{/* if we dont have pdf or viewPdf state is null */}
{!viewPdf && <>No pdf file selected</>}
</div>
</div>
);
};
export default App;
My code for rendering PDF
Related
i need to display content of the file i choose inside file selector. I have displayed it's name on navbar and when i click on it's name it takes me to the ther blank tab in which i need to have it's content displayed. I need to have only pdf and jpeg file types enabled, not other.
This uploading component is rendered inside one page, which is then rendered inside app component.
Here is my code for uploader:
import React, { useState, useEffect, useContext } from "react";
import { useDropzone } from "react-dropzone";
import Upload from "./pictures/fileUpload.png";
import { fileContext } from "../context/context";
function Uploading() {
const { setFileName } = useContext(fileContext);
const { getRootProps, getInputProps, acceptedFiles } = useDropzone({
noDrag: true,
});
// Logging the selected files to the console. //
console.log(acceptedFiles);
useEffect(() => {
if (acceptedFiles.length === 0) {
console.log("No Uploaded Files. Upload .pdf or .jpeg file !");
} else {
setFileName(acceptedFiles[0].name);
window.localStorage.setItem("fileName", `${acceptedFiles[0].name}`);
}
}, [acceptedFiles]);
return (
<section className="container w-full h-full text-center">
<div {...getRootProps({ className: "dropzone h-full" })}>
<input
{...getInputProps()}
type="file"
/>
<img src={Upload} className="mx-auto cursor-pointer " alt="" />
<label class="text-2xl">Choose Document</label>
</div>
</section>
);
}
export default Uploading;
You can use simple JavaScript for that.
let file = $('#photo')[0].files[0];
let reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function(e){
let blob = new Blob([e.target.result]);
window.URL = window.URL || window.webkitURL;
let blobURL = window.URL.createObjectURL(blob);
let image = new Image();
image.src = blobURL;
image.onload = function() {
//add the image element to the target container
}
}
Sorry for not showing exactly what you wanted. I've showed how you can get image element after file upload and then do stuff with it.
I am building an image upload form using Next.js/React.js, and I want the user to be able to assign a tag to each upload. I also want to show the image preview using 'URL.createObjectURL'. The uploading works fine, but on the upload page where I try to iterate through the list of images to show the preview and show an input box to assign the tag, none of this is showing. I cannot work out why.
The code:
import { useState } from "react";
import Head from 'next/head'
import Layout, { siteTitle } from '../../components/layout'
import Image from 'next/image'
export default function PrivatePage(props) {
const [images, setImages] = useState([])
const [imageURLS, setImageURLS] = useState([])
const [tag, setTag] = useState(null)
const uploadImageToClient = (event) => {
var imageList = images
var urlList = imageURLS
if (event.target.files && event.target.files[0]) {
imageList.push(event.target.files[0]);
urlList.push(URL.createObjectURL(event.target.files[0]))
setImages(imageList);
setImageURLS(urlList);
}
};
const uploadTagToClient = (event) => {
if (event.target.value) {
const i = event.target.value;
setTag(i);
}
};
const uploadToServer = async (event) => {
const body = new FormData()
images.map((file, index) => {
body.append(`file${index}`, file);
});
body.append("tag", tag)
const response = await fetch("/api/file", {
method: "POST",
body
});
};
return (
<Layout home>
<Head>
<title>{siteTitle}</title>
</Head>
<div className="container">
<div className="row">
<h4>Select Images</h4>
<div className="col">
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
<input type="file" className="btn btn-outline-success-inverse" onChange={uploadImageToClient} />
</div>
<div className="col">
</div>
<button
className="btn btn-outline-success-inverse"
type="submit"
onClick={uploadToServer}
>
Send to server
</button>
{images.map((file, index) => {
return (
<div class="row ">
<lead>{file.name}</lead>
<input type="text" onChange={uploadTagToClient} />
<image src={imageURLS[index]}/>
</div>)
})}
</div>
</div>
</Layout>
);
}
To clarify, nothing inside images.map is showing when I select images.
The issue happens because you're mutating the arrays you have in state with imageList.push and urlList.push which React doesn't pick up. This means state doesn't actually get updated and a re-render doesn't occur.
To fix it, rather than mutating those state arrays you need to create new ones when updating them.
const uploadImageToClient = (event) => {
if (event.target.files && event.target.files[0]) {
setImages((imageList) => [...imageList, event.target.files[0]]);
setImageURLS((urlList) => [
...urlList,
URL.createObjectURL(event.target.files[0])
]);
}
};
Unrelated to the main issue, you have several minor issues inside the render part of your component, specifically inside images.map.
You need to set a key prop on the outer <div> element;
The <lead> element doesn't exist, and needs to be replaced with a valid element;
The <image> element also doesn't exist, you probably meant <img> or <Image> (from next/image).
You can handle image upload with multiple image preview with following code.
const handleFile = (e) => {
setMessage("");
let file = e.target.files;
for (let i = 0; i < file.length; i++) {
const fileType = file[i]['type'];
const validImageTypes = ['image/gif', 'image/jpeg', 'image/png'];
if (validImageTypes.includes(fileType)) {
setFile([...files,file[i]]);
} else {
setMessage("only images accepted");
}
}
};
Follow this snippet https://bbbootstrap.com/snippets/multiple-image-upload-preview-and-remove-92816546
I'm trying to create a file upload for STL files. The code below works as in that in the onDrop function the console.log shows an empty array for all other file types and the files if they are of type STL. So it does what it's supposed to do.
However the line
{isDragReject && 'File type not accepted, sorry!'}
always fires, even for stl files. Which certainly would confuse the user.
import React, { useCallback } from 'react';
import Dropzone, { useDropzone } from 'react-dropzone';
const FileDropzone = () => {
const maxSize = 100000000;
const onDrop = useCallback((acceptedFiles) => {
console.log(acceptedFiles);
}, []);
const {
isDragActive,
getRootProps,
getInputProps,
isDragReject,
acceptedFiles,
rejectedFiles,
} = useDropzone({
onDrop,
accept: '.stl',
minSize: 0,
maxSize,
});
const isFileTooLarge =
rejectedFiles &&
rejectedFiles.length > 0 &&
rejectedFiles[0].size > maxSize;
return (
<div className="container text-center mt-5">
<div {...getRootProps()}>
<input {...getInputProps()} />
{!isDragActive && 'Click here or drop a file to upload!'}
{isDragActive && !isDragReject && "Drop it like it's hot!"}
{isDragReject && 'File type not accepted, sorry!'}
{isFileTooLarge && (
<div className="text-danger mt-2">File is too large.</div>
)}
</div>
</div>
);
};
export default FileDropzone;
This is a bug, please see details here: https://github.com/react-dropzone/react-dropzone/issues/888
Solution: Downgrade to previous version of DropZone.
Basically, in my use case, I'm receiving File Object from Parent to the Download component through the props and I want to be able to straight away download it. Is there a way I can do that ?
Please find below reference. [I know, it looks a bit weird in Parent component to select file using file input and to be able to download it just then. This is just for the sake of simplicity.]
Download Button
const DocumentMessage = ({file, type, label, ...props}) => {
return (
<Button as='a' style={{textDecoration: 'none'}} target='_blank' href={file} {...props}>{label}</Button>
);
}
Example of Parent Component where it's used -
export const documentMessageDefault = () => {
const [selectedFile, setSelectedFile] = useState(null);
const handleFileChange = (e) => {
file = e.target.files[0];
setSelectedFile(file);
}
return (
<div>
<input type='file' onChange={handleFileChange} />
<DocumentMessage file={selectedFile} label= 'Download File' />
</div>
);
}
A component of a library I'm using is essentially a wrapper around <input type=file>, which manages the buttons to upload a new image, change it or remove it, while also providing a thumbnail of it. This is its simplified definition:
import defaultImage from "assets/images/default_image.jpg";
export default function ImageUpload(props) {
const { image, setImage } = props;
const [imagePreviewUrl, setImagePreviewUrl] = useState(defaultImage);
let fileInput = React.createRef();
const handleImageChange = e => {
e.preventDefault();
let reader = new FileReader();
const imageFile = e.target.files[0];
reader.onloadend = () => {
const image = reader.result;
setImage(image);
setImagePreviewUrl(image);
};
reader.readAsDataURL(imageFile);
};
const handleClick = () => {
fileInput.current.click();
};
const handleRemove = () => {
setImage("");
setImagePreviewUrl(defaultImage);
fileInput.current.value = null;
};
return (
<div>
<input
type="file"
onChange={handleImageChange}
ref={fileInput}
/>
<div>
<img src={imagePreviewUrl} />
</div>
<div>
{image === null ? (
<Button onClick={() => handleClick()}> {"Select image"} </Button>
) : (
<span>
<Button {...changeButtonProps} onClick={() => handleClick()}> Change </Button>
<Button {...removeButtonProps} onClick={() => handleRemove()}> Remove </Button>
</span>
)}
</div>
</div>
);
}
The parent is basically collecting this and other components's states and stores them into a redux store. All works well, until I try to clear the preview image.
As input is not a controlled component in react, I'm not quite sure how to do that.
I though about passing the setImagePreviewUrl as a prop, but I'm not entirely sure how to implement it.
How can I clear that preview image?
If you try hiding the preview image than add condition like this:
<div>
{imagePreviewUrl && <img src={imagePreviewUrl} /> }
</div>
See full example by your code in the playground: https://jscomplete.com/playground/s524255