How to upload images from remix to sanity.io? - node.js

I am trying to upload images from a remix app to sanity.io from a server action where I am sending data through form submit using encode type multipart/formData.
I cannot understand how to parse the file correctly from formData to send to sanity.
I have looked at unstable_parseMultipartFormData and am using the following code for the main upload:
const imageAsset = await sanityClient.assets.upload(
'image',
// <not sure what to put here>,
{
contentType: profilePic.type,
filename: `${name}-profile-pic`,
}
);
const sanityDoc = {
_type: 'profilePic',
image: {
_type: 'image',
asset: {
_type: 'reference',
_ref: imageAsset._id,
},
},
name,
email,
isOrganisation: false,
};
await sanityClient.create(sanityDoc);
Thanks for the help!

Related

Download a file to a writestream but go directly to a readstream in node.js

I'm trying to upload a file to youtube, using the the googleapis/youtube library. All examples online point to uploading a file from your local filesystem, the code is like this:
const data = await yt.videos.insert({
resource: {
// Video title and description
snippet: {
title: 'test',
description: 'test desc',
},
// I don't want to spam my subscribers
status: {
privacyStatus: 'public',
},
},
// This is for the callback function
part: 'snippet,status',
// Create the readable stream to upload the video
media: {
body: fs.createReadStream(
path.resolve(
'/Users/username/file.mp4',
),
),
},
});
The above code works, however, I want to download a file from a url and immediately pipe it into the call above, replacing the fs.createReadStream and bypassing the local filesystem entirely so I don't have to do cleanup afterwards or hit space limitations in a lambda (/tmp is 500MB max).
I read somewhere else you could use a stream.PassThrough stream but I'm not sure if there's a better way to do this.
TL;DR:
I want to go from
URL -> writestream -> filesystem -> readstream -> youtube
to
URL -> writestream -> youtube
With axios, you should be able to use the incoming stream directly:
const response = await axios({
method: 'get',
url: someURL,
responseType: 'stream'
});
And, then use that stream directly:
media: {
body: response.data
},

How to send FormData in MERN Stack project with TS

I'm trying to send FormData to my backend but when I console.log the req.body it's empty object and I don't know why.
Here is my frontend request:
const createProduct = (e: any) => {
e.preventDefault();
const data = new FormData()
data.append("name", name)
data.append("description", description)
data.append("price", price)
for (const colorAndImage of colorsAndImages) {
data.append('images', colorAndImage.images[0]);
data.append('colors', colorAndImage.colors);
}
data.append("type", type)
fetch('http://localhost:4000/products/create', {
method: 'POST',
body: data
})
Here is how the image file looks like in the console:
File {name: 'iphone_13_pro_max_gold_pdp_image_position-1a__wwen_5.jpg', lastModified: 1642862621798, lastModifiedDate: Sat Jan 22 2022 16:43:41 GMT+0200 (Eastern European Standard Time), webkitRelativePath: '', size: 22194, …}
Here is what I'm sending in the request in Network tab:
name: sdf
description: sdf
price: 234
images: (binary)
colors: red
type: sdf
Here is the controller in backend side:
productController.post('/create', async (req: Request, res: Response) => {
console.log(req)
try {
const data = req.body;
let product = await create(data)
res.status(201).json(product)
} catch (error) {
console.log(error);
//res.status(500).json({error: error})
}
})
And what I see when I try to console.log the request:
{
name: undefined,
description: undefined,
price: undefined,
colors: undefined,
images: undefined,
type: undefined,
likes: undefined
}
Error: Product validation failed: name: Path `name` is required., description: Path `description` is required., price: Path `price` is required., type: Path `type` is required.
My express config:
const corsConfig: cors.CorsOptions = {
credentials: true,
origin: ['http://localhost:3000', 'http://localhost:2000']
}
export default function (app: Application) {
app.use(cors(corsConfig))
app.use(cookieParser());
app.use(express.urlencoded({ extended: false }));
app.use(express.json())
app.use(auth())
}
Not sure why are you using FormData in sending raw data from frontend to backend.
FormData is generally used when you have to send (upload) files. For simple data you can just send JSON object.
Express by default can't parse multipart/form-data, you will have to install a middleware like multer in order to get the data or you can update your data structure in frontend.
let dataToSend = {
name: name,
description: description,
price: price
// Rest of the properties
}

How to upload a file from an export in Google Drive API without saving the file

I'm exporting a file sheet file from drive and uploading it back to drive in pdf format. The problem is that in order to upload it I need to save it to file on a server first.
I've tried to read a response from drive.files.export in fs.createReadStream, but it didn't work. Is there another way?
const res = await drive.files.export(
{ fileId, mimeType: "application/pdf" }
);
var media = {
mimeType: 'application/pdf',
body: fs.createReadStream(res) // TypeError [ERR_INVALID_ARG_TYPE]: The "path" argument must be of type string or an instance of Buffer or URL. Received an instance of Object
};
const resCreate = await drive.files.create({
uploadType: "media",
media: media,
resource: fileMetadata,
fields: "id"
}, function (err, file) {...});
I believe your goal as follows.
You want to export a Google Spreadsheet as the PDF data and want to upload it to Google Drive.
At that time, you want to achieve this without creating a file.
You want to achieve this using googleapis for Node.js.
In this case, how about the following modification?
Modified script:
const res = await drive.files.export(
{ fileId, mimeType: "application/pdf" },
{ responseType: "stream" }
);
var media = {
mimeType: "application/pdf",
body: res.data,
};
const resCreate = await drive.files.create({
uploadType: "media",
media: media,
resource: fileMetadata,
fields: "id",
});
console.log(resCreate.data.id);
Before you use this modified script, please set fileId and fileMetadata.
The exported file is retrieved the stream data with responseType: "stream". By this, the returned data can be used for media.
Reference:
google-api-nodejs-client

NodeJs Buffer is not attached to Nodemailer file

Currently on my project I am dealing with a situation and I feel it is a problem of syntax but I am not getting the right answer and kinda feeling stressed and tilt over it for the last three days.
I am using three libraries NodeJs, Html-pdf(phantomJS) and Nodemailer.
this is the block of code in question (everything is inside a major async function that is a resolver of graphQl)
await pdf.create(html, options).toBuffer(async (err, buffer) => {
//i = new ;
//console.log(typeof(buffer))
await buffer;
await transporter.sendMail({
from: '"Example" example#example.com', // sender address
to: `${a1.dataValues.email}`, // list of receivers
subject: `${text2}`, // Subject line
text: `${text}`, // plain text body
html: `<p> ${text} </p>`, // html body
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: buffer,
contentType: "application/pdf",
},
],
});
});
The email is sent but I get this in the browser
The attachment is sent but I guess it is "undefined" and I have been struggling with finding a way to fix this. Can someone please help me?
I tried as well to do
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer.from(buffer),
contentType: "application/pdf",
},
],
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer(buffer),
contentType: "application/pdf",
},
],
attachments: [
{
filename: `${a1.dataValues.kitID}.pdf`,
content: new buffer.from(buffer,'base64'),
contentType: "application/pdf",
},
],
but all of those crash the app in Azure. The all thing works perfectly locally, but not when deployed.

Google drive API: can't upload files to the root folder using nodejs

I'm using google drive API to upload files.
It works if I specify the id of subfolders. But when I pass the id of the root folder, the files are not uploaded.
Here is my request:
drive.files.create(
{
auth: this.ggToken,
fields: 'id',
supportsAllDrives: true,
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root', '0AASRHiHHtzxrUk9PVA'],
},
},
(e: Error, f: any) => {
if (e) {
console.error(e);
}
console.log(f);
},
);
0AASRHiHHtzxrUk9PVA is the ID of the root folder (I get it by using drive.files.get API)
So what's wrong with my code? How can I upload files to the root folder?
Thanks.
Update 1
Here is my script:
ggToken: JWT;
constructor() {
this.ggToken = new google.auth.JWT(
process.env.GG_DRIVE_CLIENT_EMAIL,
null,
process.env.GG_DRIVE_PRIVATE_KEY,
['https://www.googleapis.com/auth/drive'],
null,
);
}
async uploadFiles(file: any) {
const credentials = await this.ggToken.authorize();
this.ggToken.setCredentials(credentials);
const uploadedFile = await drive.files.create({
auth: this.ggToken,
fields:
'id, name, mimeType, webViewLink, webContentLink, iconLink, size, originalFilename',
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root'],
properties: {},
},
});
return {
driveResource: uploadedFile.data,
};
}
I got process.env.GG_DRIVE_CLIENT_EMAIL and process.env.GG_DRIVE_PRIVATE_KEY from the json file after creating server account key.
Thank #Tanaike very much.
Here is his answer:
Thank you for replying and adding the script. I could understand about the reason of your issue. The service account is different from your own account. So the Google Drive of service account is different from that of your account. By this, when a file is uploaded to the root folder using your script, the file is uploaded to the root folder of the Drive of service account. By this, the file cannot be seen at the drive of your account. If you want to upload the file to the root folder of the Drive of your account, please use OAuth2
With Oauth2, I can upload files to the root folder.
Here is my code:
oauth2Client: OAuth2Client;
constructor() {
this.oauth2Client = new google.auth.OAuth2({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: 'https://developers.google.com/oauthplayground',
});
this.oauth2Client.setCredentials({
access_token: process.env.ACCESS_TOKEN,
refresh_token: process.env.REFRESH_TOKEN,
});
}
async uploadFiles(file: any) {
const uploadedFile = await drive.files.create({
auth: this.oauth2Client,
fields:
'id, name, mimeType, webViewLink, webContentLink, iconLink, size, originalFilename',
media: {
body: this.convertBufferToStream(file.buffer),
},
requestBody: {
mimeType: file.mimetype,
name: file.originalname,
parents: ['root'],
},
});
return {
driveResource: uploadedFile.data,
};
}

Resources