How to know file type of some file uploaded in a form with nodeJS? - node.js

I would like to know the type of file obtained with req.files in NodeJS. I need this because the file uploaded has to be a photo for the well work of the app. It not only helps me to check that it is not a .jpg since you can make a .txt and change the extension.
The form is the following:
form(class="form add-form space-down" method="POST" enctype="multipart/form-data")
div.title
h1 UPLOAD NEW PROGRESS
div.form-group
label(for="weight") Weight:
input(type="number" name="weight" class="form-control" placeholder="Enter your weight")
div.form-group
label(for="front") Upload a front photo
input(type="file" name="front" accept="image/*")
div.form-group
label(for="from_side") Upload a from side photo
input(type="file" name="from_side" accept="image/*")
div.form-group
label(for="backwards") Upload a backwards photo
input(type="file" name="backwards" accept="image/*")
And the router handle is the following to obtain the photos uploaded:
routerProgress.post("/home/upload-progress", (req, res) => {
const front = req.files.front;
const from_side = req.files.from_side;
const backwards = req.files.backwards;
}
How can I be sure that front, from_side and backwards are photos?
If anyone has any idea how to do it, I would be very grateful if you could help me.

You can do something like this. Create a function which returns the extension of the file and you can check if it is a valid image extension or not.
routerProgress.post("/home/upload-progress", (req, res) => {
const front = getFileExtension(req.files.front);
const from_side = getFileExtension(req.files.from_side);
const backwards = getFileExtension(req.files.backwards);
if (front) {
// its an image, do something
}
}
function getFileExtension (filename) {
const allowedFileExt = ['JPEG', 'JPG', 'PNG', 'GIF', 'TIFF', 'PSD', 'PDF']; // you can add as per your requirement
const fileExt = /[^.]+$/.exec(filename);
return allowedFileExt.includes(fileExt[0].toUpperCase());
}
If that is the case, you should use mmmagic, it checks the content of the file instead checking only the extension. Try using this lib it will be more useful for your use case. Also, take a look at this npm package image-type

Related

Creating Video and Audio Upload Feature

I’m in the beginning stages of planning out my final Capstone project for my Bootcamp.
Two of the features I would like to include are the ability to upload:
Audio
Video
I will be using React.JS for Front-End and Python / Django for server side.
Any suggestions or recommendations for how to approach these upload features?
I’m currently beginning researching how to do this.
you can use html tag input on react
const [file , setFile] = useState();
<input
type="file"
id="fileInput"
onChange={(e) => setFile(e.target.files[0])}
/>
after that you can use FormData() to create file format then send file to api
const data = new FormData();
const filename = username + "_" + file.name;
data.append("name", filename);
data.append("file", file);
try {
api.uploadFile(data);
} catch (error) {
console.log(error);
}
at backend ( django ) I don't know how to handle file requset but you can find it easily . At react side you can use codes at top .

Node.js Express Temporary File Serving

I'm trying to do a reverse image search using googlethis on an image the user uploads. It supports reverse image searching, but only with a Google-reachable image URL. Currently, I upload the image to file.io, which deletes it after it gets downloaded.
This is the current application flow:
User POSTs file -> Server uploads file to file.io -> Google downloads the file -> Server does things with the reverse image search
However, I want to skip the middleman and have Google download files directly from the server:
User POSTs file -> Server serves file at unique URL -> Google downloads the file -> Server deletes the file -> Server does things with the reverse image search
I've looked at Serving Temporary Files with NodeJs but it just shows how to serve a file at a static endpoint. If I added a route to /unique-url, the route would stay there forever (a very slow memory leak! Probably! I'm not really sure!)
The only way I can think of is to save each file with a UUID and add a parameter: /download?id=1234567890, which would probably work, but if possible, I want to do things in memory.
So:
How do I do this using normal files?
How do I do this in-memory?
Currently working (pseudo) code:
app.post('/', (req, res) => {
const imagePath = saveImageTemporarily(req)
const tempUrl = uploadToFileIo(imagePath)
const reverseImageResults = reverseGoogleSearch(tempUrl)
deleteFile(imagePath)
doThingsWithResults(reverseImageResults).then((result) => { res.send(result) })
}
The other answer is a good one if you are able to use Redis -- it offers lots of helpful features like setting a time-to-live on entries so they're disposed of automatically. But if you can't use Redis...
The basic idea here is that you want to expose a (temporary) URL like example.com/image/123456 from which Google can download an image. You want to store the image in memory until after Google accesses it. So it sounds like there are two (related) parts to this question:
Store the file in memory temporarily
Rather than saving it to a file, why not create a Buffer holding the image data. Once you're done with it, release your reference to the buffer and the Node garbage collector will dispose of it.
let image = Buffer.from(myImageData);
// do something with the image
image = null; // the garbage collector will dispose of it now
Serve the file when Google asks for it
This is a straightforward route which determines which image to serve based on a route parameter. The query parameter you mention will work, and there's nothing wrong with that. Or you could do it as a route parameter:
app.get('/image/:id', (req, res) => {
const id = req.params.id;
res.status(200).send(/* send the image data here */);
});
Putting it all together
It might look something like this:
// store image buffers here
const imageStore = {};
app.post('/image', (req, res) => {
// get your image data here; there are a number of ways to do this,
// so I leave it up to you
const imageData = req.body;
// and generate the ID however you want
const imageId = generateUuid();
// save the image in your store
imageStore[imageId] = imageData;
// return the image ID to the client
res.status(200).send(imageId);
});
app.get('/image/:id', (req, res) => {
const imageId = req.params.id;
// I don't know off the top of my head how to correctly send an image
// like this, so I'll leave it to you to figure out. You'll also need to
// set the appropriate headers so Google recognizes that it's an image
res.status(200).send(imageStore[imageid]);
// done sending? delete it!
delete imageStore[imageId];
});
I would use REDIS for the in-memory DB, and on the server, I would transform the image to base64 to store it in Redis.
In Redis, you can also set TTL on the images.
Check my code below
import {
nanoid
} from 'nanoid'
function base64_encode(file) {
// read binary data
var bitmap = fs.readFileSync(file);
// convert binary data to base64 encoded string
return new Buffer(bitmap).toString('base64');
}
app.post('/', async(req, res) => {
const client = redisClient;
const imagePath = saveImageTemporarily(req)
//const tempUrl = uploadToFileIo(imagePath)
var base64str = base64_encode(imagePath);
const id = nanoid()
await client.set(id, JSON.stringify({
id,
image: base64str
}));
const reverseImageResults = reverseGoogleSearch(JSON.parse(await client.get(id)).image)
await client.del(id);
doThingsWithResults(reverseImageResults).then((result) => {
res.send(result)
})
}

Why React doesn't upload image to server?

I have an app using react and express on the backend and multer to manage the upload. The server side is running properly when I make tests using postman, but if trait to send an image from react the result is unexpected. In that case the file doesn't appear in the uploads folder, however with postman is immediatly.
UploadPage,jsx
const { register, handleSubmit } = useForm();
const onSubmit = async (data) => {
const formData = new FormData();
formData.append('petimage', data.petimage);
try {
const res = await axios.post('/api/petregister', formData);
console.log(res)
} catch (error) {
setError(error.response.data.error);
setTimeout(() => {
setError("");
}, 5000);
}
}
return (
<Container className="mt-5">
<Form onSubmit={handleSubmit(onSubmit)}>
<Form.Group controlId="formFile" className="mb-3">
<Form.Label>Imagen de tu Mascota</Form.Label>
<Form.Control type="file"
label="Select image"
name="petimage"
{...register("petimage")}
/>
</Form.Group>
<Button variant="primary" type="submit">
Submit
</Button>
</Form>
</Container>
Google Response
The fields with name petimage are the same that I expecified in the backend and used these in the postman tests.
Edit
const store = require('../middlewares/multer');
route.post('/petregister', store.array('petimage', 12), petregister);
The last section of code is the route that is linked with the multer midleware asigned to ssave the images.
When you are making a API call to the backend, it will upload the image to the specific folder that you are defining in the backend like :
const multer = require('multer');
const upload = multer({ dest: 'folder path' });
I think you are getting results unexpected because the name for the image you are giving in formData formData.append('petimage', data.petimage); i.e petimage, it should be the same in the multer fileupload method. You haven't shared the backend code. So, I'm hoping that it may be like this:
var fileUpload = upload.single('petimage'); when the name is the same it will work fine.
If the image is of big size, you can compress it. Please visit this link, it will help you for sure.
https://dev.to/franciscomendes10866/image-compression-with-node-js-4d7h
You can try:
Remove
formData.append('petimage', data.petimage);
and use instead
data.petimage.forEach(pet => formData.append("petimage", pet))
The solution was trait the image as an object. The code is the next:
Object.values(data.petimage).forEach(pet => formData.append('petimage', pet))
Then it worked as expected.

How to get filepath of uploaded file to createReadStream

I am using Nodejs readStream.pipe(writeStream). How can I get the full path of the file I am uploading and assign it to createReadStream. I get only filename and When the file is in nodejs root directory it is fine no error but when I upload from anywhere else I get an error:
events.js:183
throw er; // Unhandled 'error' event
^
Error: ENOENT: no such file or directory, open 'C:\Users\Esen\Documents\GitHub\gbmsturbo\nike.png'
I know that this happens because nike.png that I am uploading is not in "C:\Users\Esen\Documents\GitHub\gbmsturbo\".
It is inside this folder:
"C:\Users\Esen\Documents\GitHub\filestoupload"
I tried
function throwErrorDontCrash() {
process.on('uncaughtException', function (err) {
console.error(err.message)
})
}
and this prevents nodejs crash and uploads the file but with empty content (0 byte)
router.post('/upload', (req, res) => {
var filePath = req.body.file
var ext = path.extname(filePath)
var filename = path.basename(filePath, ext)
var newVerFilePath = "public/uploads/" + newVerFileName+ext
const readStream = fs.createReadStream(filePath)
throwErrorDontCrash()
const writeStream = fs.createWriteStream(newVerFilePath)
readStream.pipe(writeStream)
function throwErrorDontCrash() {
process.on('uncaughtException', function (err) {
//console.error(err.message)
})
}
and here is my form file
<form class="center" action="/upload" method="post">
<input id="file" type="file" name="file" required encrypt="multipart/form-data"/>
<input type="submit" value="UPLOAD">
I want filePath to include the directory path where ever the file is uploaded when user clicks on Choose or Browse button.
Currently, filePath gets only filename such as nike.png and my expectation is
to get "C:/Users/Esen/Documents/GitHub/filestoupload/nike.png"
it looks like you are writing an express app on nodejs. Your issue is here:
const readStream = fs.createReadStream(filePath);
I think that you believe this is how you "read" the file the user is uploading, but that's actually not possible - the "fs" module doesn't exist in the browser. The user navigating your website is uploading the image from their computer via a form, which means the image is coming in from the HTTP request (the req object), not from the file system.
(This can be confusing because in your case, you probably are running this express app locally on your machine, but it's easier to imagine it in production - the express app runs on a big server somewhere, and is a different computer than your user's computer, where the file being uploaded lives.)
Check out the related S.O. question How to upload, display and save images using node.js and express. Also, see the tutorial Upload Files or Images to Server using Nodejs.

Image not Uploading while using multer single image input option?

I am using multer to upload single image...
router.post('/register', upload.any(), function(req,res,next){
var img = req.files;
}
If I print the img the it is showing the object properly( originalname, mimeType, size etc) but when i trying to add the propertys in individual variable ( originalname = img.originanae;/req.files.originalename;) then originalname is showing undefined.
Why ??
use req.files[0].originalname instead of req.files.originalname

Resources