How to access files on digital ocean spaces with multer - node.js

I have managed to upload pdf files on digital ocean spaces with a node js app as shown below.
I don't know how to then access those files and display them to the user. I got the code below from this tutorial, object storage file upload, but there isn't an example on how to then access the files.
When I just try to access them with their url, I just get white space.
I have made them public but still get absolutely nothing trying to access them from the url.
Is there a way to access the files using multer still, do I have to make a get request with the RESTFul API? How do I access files stored in a digital ocean spaces?
This is how I upload files
const aws = require("aws-sdk");
const multer = require("multer");
const multerS3 = require("multer-s3");
const spaceEndPoint = new aws.Endpoint("ams3.digitaloceanspaces.com");
const s3 = new aws.S3({
endpoint:spaceEndPoint
})
const upload = multer({
storage:multerS3({
s3:s3,
bucket: "fileRepo",
acl:"public-read",
key:function(request, file, cb){
console.log(file);
cb(null, file.originalname);
}
})
}).array("upload",1);
router.get("uploadFile", function(req,res){
upload(req, res, function(error){
if(error){
console.log(error);
return res.redirect("/");
}
});
})
And this is how I try to retrive the pdfs
router.get("/contentPage", function(req, res){
var fileName = req.body.department;
var directory = "https://fileRepo.ams3.digitaloceanspaces.com/" + fileName + ".pdf";
res.render("fileview", {dir: directory});
})
<div id="departmentListWrapper" class="container">
<embed src="<%= dir %>" width="800px" height="2100px" />
</div>
Can anyone tell me what the issue might be with retrieving these files?

Try this code snippet, works fine for me
import multer from 'multer';
import multerS3 from 'multer-s3';
aws.config.update({
accessKeyId: 'your access key',
secretAccessKey: 'your secret key'
});
var filepath = "path of files folder"
// Create an S3 client setting the Endpoint to DigitalOcean Spaces
var spacesEndpoint = new aws.Endpoint('nyc3.digitaloceanspaces.com');
var s3 = new aws.S3({endpoint: spacesEndpoint});
var params = {
Bucket: bucketName,
Key: keyName,
Body: fs.createReadStream(filepath),
ACL: 'public-read'
};
s3.putObject(params, async function(err, data) {
if (err) {
console.log(err);
} else {
console.log(data);
}
});

It turns out that digital ocean did not know what type of file was being uploaded, I had to make it so that the file type is read before it is uploaded so that the browser knew what type of file I was trying to upload by adding the line
contentType: multerS3.AUTO_CONTENT_TYPE
to the multer object so that it looks like this.
const upload = multer({
storage:multerS3({
s3:s3,
bucket: "fileRepo",
contentType: multerS3.AUTO_CONTENT_TYPE
acl:"public-read",
key:function(request, file, cb){
console.log(file);
cb(null, file.originalname);
}
})
}).array("upload",1);

Related

Not getting the success response after video upload on s3 node.js

I am using the node.js as a backend and needs to upload the video on Amazon S3.
For that I am using the multer module but I need to know the efficient and standard way for uploading the video.
Generally we see that when we upload anything on the any good platfrom then there is a proper mechanism for uploading the video like:
When video is on uploading state, user get the response that how much percent is left for uploading the video
After uploading the video user get the response that video is uploaded successfully.
There is a handler which allow specific type of format to allow video.
There is a few limit size also which warn the user that maximum size is 20mb or 50 mb.
I am bit struggling about the good tutorial but unable to find as everywhere is sharing the tutorials about the image upload. So I thought I will raise the question which will help many others also regarding the same
I have implemented the process by which video will be uploaded on S3 but not getting the response after upload. It directly return the response and video will be uploaded in background.
I am sharing my implementation:
customapi.js file
const express = require('express');
const router = express.Router();
const helper = require('./file-upload');
const videoHandler = require('./videohandler');
// Post the video
router.post(
'/uploadvideo',
helper.single('media'),
videoHandler .uploadVideo
);
file-upload.js file
const AWS = require('aws-sdk')
const multer = require('multer')
const multerS3 = require('multer-s3')
const uuid = require('uuid/v1');
AWS.config.update({
accessKeyId: process.env.keyId,
secretAccessKey: process.env.accessKey,
});
const s3 = new AWS.S3();
const upload = multer({
storage: multerS3({
s3:s3,
bucket: process.env.bucketname,
acl: 'public-read',
contentType: multerS3.AUTO_CONTENT_TYPE,
metadata: function (req, file, cb) {
cb(null, {fieldName: file.fieldname})
},
key: function (req, file, cb) {
console.log(file) // This will print the filename which we can search in s3.
cb(null, uuid()+file.originalname)
console.log(uuid() + file.originalname)
}
})
})
videohandler.js file
module.exports = {
uploadVid: async (req, res) => {
try {
return res.send({message: "Done"})
} catch (error) {
console.log(error);
return res.send({message: "Error"})
}
}
}
I know what is happening exactly by which I am getting the response instantly instead of after the file upload.
When api calls -> helper.single('media') will invoke and process to start performing the task -> meanwhile videoHandler.uploadVideo will also called which directly send the response as there is no callback which understand the uploading process and return response accordingly.
Please share the best and efficient way as it supports many people also who is struggling for uploading the video.
Any help or suggestion is really appreciated.
Update Question
Many people sharing the way that video first upload on local disk and then it upload on S3. I need to know that is it a good behaviour. I mean for performing the task we need to do the double work instead of directly upload. It will consume the bandwidth and storage of the application on server.
I could handle it in this way. check whether is it applicable to your scenario.
App.js
router.post("/upload_service",
(req, res, next) => {
const upload = UploadController.upload.single('file')
upload(req, res, (err) => {
if (err) {
const error = new Error('Image upload error');
return next(error);
}
return next()
})
},
SomeController.createMethod)
UploadController.js
const multer = require('multer')
const multerS3 = require('multer-s3')
const AWS = require('aws-sdk')
const upload = multer({
storage: multerS3({
s3: new AWS.S3(),
bucket: 'bucket_name',
metadata: function (req, file, cb) {
cb(null, { fieldName: file.fieldname });
},
key: function (req, file, cb) {
const file_name_timestamp = Date.now().toString()
cb(null, `path/${file_name_timestamp}.${String(file.mimetype).split('/').pop()}`);
},
ContentType: "application/octet-stream",
})
})
module.exports = {
upload
}
SomeController.js
const createMethod = async function (req, res) {
if (!req.file) {
res.send('File missing')
}
}
Once file upload middleware was success, SomeController.createMethod will be triggered. using req.file, creation data can be visible.

Upload multiple images on Amazon S3 using Node.js

I am trying to upload images to amazon s3. However req.files is still undefined. I don't understand where is my code going wrong. The images are sent from Angular frontend. 'artFileLocations' is array of files in FormData. How do I fix this?
const router = require('express').Router()
const AWS = require('aws-sdk')
const multer = require('multer');
const multerS3 = require('multer-s3');
AWS.config.update({
accessKeyId: process.env.AWS_ACCESS_KEY_ID,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY
});
var s3 = new AWS.S3();
// Unique name of aws s3 bucket created
const myBucket = 'nish.images';
// Multer upload (Use multer-s3 to save directly to AWS instead of locally)
var upload = multer({
storage: multerS3({
s3: s3,
bucket: myBucket,
// Set public read permissions
acl: 'public-read',
// Set key/ filename as original uploaded name
key: function (req, file, cb) {
cb(null, file.originalname)
}
})
})
router.post('/', upload.array('artFileLocations', 3), function(req, res) {
console.log('Reaching addPainting')
console.log(req.body, req.files)
});
module.exports = router;
Your code looks fine.
but can you update your HTML file like this:
<input type="file" name="artFileLocations" multiple>
If you already update your HTML file, then I think you don't face any other error.
Try uploading single file and check whether your req.files is working or not. I also encountered this error where the multiple file upload was not working. So, check if the req.files is working for single file upload.

How to receive a file from frontend using post method and upload it to google cloud?

I am trying to upload a file to google storage bucket. The file will be sent to the nodejs backend route using post method and form-data. I have read google storage documents regarding uploading the files.
I am not getting the idea about how should i receive the file and assign it to filename variable so that it can be uploaded to google storage bucket. Something just like this.
const {Storage} = require('#google-cloud/storage');
// Creates a client
const storage = new Storage();
test: async (req, res, next) => {
const filename = req.body.file
// const bucketName = 'Name of a bucket, e.g. my-bucket';
// const filename = 'Local file to upload, e.g./local/path/to/file.txt';
await storage.bucket(bucketName).upload(filename, {
gzip: true,
metadata: {
cacheControl: 'public, max-age=31536000',
},
});
console.log(`${filename} uploaded to ${bucketName}.`);
}
}
I have created the code below from the samples of node-formidable library to handle form data and the snippets for nodejs-Storage library. Indeed I tested it and works pretty well, tough you may want to customize filenames and the like.
const express = require('express');
const formidable = require('formidable');
const {Storage} = require('#google-cloud/storage');
const bucket = 'yourBucketName';
async function uploadFile(bucket, filename) {
const storage = new Storage();
const params = {metadata: {cacheControl: 'public, max-age=31536000'}};
await storage.bucket(bucket).upload(filename, params);
console.log(`${filename} uploaded to ${bucket}.`);
}
const app = express();
app.get('/', (req, res) => {
res.send(`
<h2>With <code>"express"</code> npm package</h2>
<form action="/api/upload" enctype="multipart/form-data" method="post">
<div>Text field title: <input type="text" name="title" /></div>
<div>File: <input type="file" name="someExpressFiles" multiple="multiple" /></div>
<input type="submit" value="Upload" />
</form>
`);
});
app.post('/api/upload', (req, res, next) => {
const form = formidable();
form.parse(req, (err, fields, files) => {
if (err) {
next(err);
return;
}
let imgPath = files.someExpressFiles.path;
uploadFile(bucket, imgPath).catch(console.error);
res.json({ fields, files });
});
});
app.listen(8080, () => {
console.log('Server listening on http://localhost:8080 ...');
});
The gg docs solution only works in development.
If you are in production, your Nodejs won't aware of file path
So follow these steps may help you:
Step 1: Encode your image to base64 and send it to backend.
Step 2: From your backend, you have to decode this base64 and upload it to gg cloud storage
this link is helpful: How do I upload a base64 encoded image (string) directly to a Google Cloud Storage bucket using Node.js?

How to upload images to GCS bucket with multer and NodeJS?

I'm facing issues for uploading local images to my google cloud storage.
I've already tried two methods. The first one is uploading with multer
var storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, './uploads/')
},
filename: (req, file, cb) => {
cb(null, file.fieldname + '-' + Date.now())
}
});
var upload = multer({storage: storage}).single('image');
app.post('/upload',function(req,res,next){
upload(req,res,(err) => {
if(err){
console.log(err)
}else{
console.log(req.file)
}
})
})
Then, i've tried directly with GCS
var bucket = admin.storage().bucket('mybucket')
app.post('/upload',function(req,res,next){
bucket
.save(file)
.then(() => {
})
for both of these solutions , req.files is always undefined whereas req.body is a buffer like this :
<Buffer 2d 2d 2d 2d ...>
when i try to save this buffer on my GCS bucket, i the .jpg/png file is created in my bucket but it is corrupted.
I'm browsing the web seeking for a solution but i found nothing that helped me to overcome this situation.
Any advice ?
You need multer, multer-google-storage and ofcourse bodyParser if you have additional form values. You need to sent data in multipart/form-data
In your .env file
GCS_BUCKET = <bucket name>
GCLOUD_PROJECT = <project id>
GCS_KEYFILE = <key file location>
You can download key file from GCP Console>Your Project>I AM & Admin>Service Accounts
In your route
const multer = require('multer');
const multerGoogleStorage = require("multer-google-storage");
var uploadHandler = multer({
storage: multerGoogleStorage.storageEngine()
});
router.post('/', uploadHandler.single('image'), function (req, res, next) {
const body = req.body;
res.json({fileName: req.file.filename});
res.end();
}
This will store file on to GCS with name [random-string-generated-by-gcs]_[YOUR FILE NAME WITH EXTENTION]. The same can be access under the route via req.file.filename.
Documentation
Make sure you have added enctype="multipart/form-data" attribute to your form. A probable reason for req.files being undefined.

Combine multer and tinypng API in node

does anyone know how to use tinyPNG's API with multer? The docs seem deceptively simple:
var source = tinify.fromFile("unoptimized.jpg");
source.toFile("optimized.jpg");
though there's no clear indication of where this is meant to go, especially in something as convoluted as this:
var storage = multer.diskStorage(
{
destination: function (req, file, callback) {
callback(null, './uploads');
},
filename: function (req, file, callback) {
//use date to guarantee name uniqueness
callback(null, file.originalname + '-' + Date.now());
}
}
);
//.any() allows multiple file uploads
var upload = multer({ storage : storage}).any()
app.post('/api/photo', function(req,res){
upload(req,res,function(err) {
if(err) {
return res.end("Error uploading file.");
}
res.end("File is uploaded");
});
});
Where am I meant to "intercept" the file uploaded by multer so that I can compress it with tinyPNG?
Thanks in advance for the help!
Use following basic sample that changes uploaded photo/gallery files:
// Import express and multer.
var express = require('express');
var multer = require('multer');
// Setup upload.
var upload = multer({ dest: 'uploads/' });
var multipleFiles = upload.fields([{ name: 'photo', maxCount: 1 },
{ name: 'gallery', maxCount: 8 }]);
// Setup tinify.
var tinify = require("tinify");
tinify.key = "YOUR_API_KEY";
// Get request handler for '/' path.
var app = express();
app.get('/', function (req, res) {
res.setHeader("Content-Type", "text/html");
res.end(
"<form action='/api/photo' method='post' enctype='multipart/form-data'>" +
"<input type='file' name='photo' />" +
"<input type='file' name='gallery' multiple/>" +
"<input type='submit' />" +
"</form>"
);
});
// Upload file handler with '/api/photo' path.
app.post('/api/photo', multipleFiles, function (req, res) {
req.files['gallery'].forEach(function(file) {
// Your logic with tinify here.
var source = tinify.fromFile(file.path);
source.toFile(file.path + "_optimized.jpg");
});
res.end("UPLOAD COMPLETED!");
});
Feel free to change express middleware how you need it, just make sure you use upload.fields and authenticate using tinify.key = "YOUR_API_KEY";
https://github.com/expressjs/multer
https://tinypng.com/developers/reference/nodejs#compressing-images
I recently worked out a similar problem for myself using the tinify package and found the docs to be somewhat lacking.
I have a Vue front end collecting file uploads from the user using vue2dropzone. These are sent to a node / Express back end.
I have a need to compress the file and upload it to an S3 instance without storing on disk. That means using multer memory storage.
As a result there won’t be an ability to use tinify.fromFile() as there is no file stored locally.
In my images middleware:
Const multer = require(“multer”);
const tinify = require("tinify");
tinify.key = "your_key";
exports.singleFile = multer({ storage: multer.memoryStorage() }).fields([{ name: "file", maxCount: 1 }]);
exports.uploadCompImage = async (req, res, next) => {
try {
const fileName = `${req.params.name}${path.extname(req.files.file[0].originalname)}`;
const source = tinify.fromBuffer(req.files.file[0].buffer);
source.store({
service: "s3",
aws_access_key_id: "your_id",
aws_secret_access_key: "your_key
region: "your_region",
headers: {
"Cache-Control": "public"
},
path: `your_bucket/your_folder/${fileName}`
});
return res.status(200).send(`path_to_file/${fileName}`)
} catch (err) {
console.log(err);
next(err);
}
}
Then in my routes file:
Const images = require(“../middleware/images”);
// skipped several lines for brevity
productRouter
.route("/images/:name")
.post(images.singleFile, images.uploadCompImage)
This process creates a multer singleFile upload to memoryStorage, making the file available at req.files.file[0] (req.files[“file”] because I specified “file” as the name in multer fields, loop through this array if uploading multiple).
After setting that up I get the file name, set the source by using tinify to read from req.files.file[0].buffer as a buffer.
Then I set the source to my s3 instance and send back a public link to the file.
Hopefully this answer helps you. I could definitely see altering the process to change where the file goes or even write it to disk by altering the multer options.

Resources