How to upload formdata file to Pinata? - next.js

I am trying to upload a base64 file to Pinata, but my Formdata seems malformed for unknown reason.
I create the formdata in index.js and send to the NextJS api as so:
// file is a base64 string
_createNFTFormDataFile = async (name, description, file) => {
try {
const formData = new FormData()
formData.append('name', name)
formData.append('description', description)
formData.append('file', file)
// formdata logs correctly
for (let pair of formData.entries()) {
console.log(pair[0]+ ', ' + pair[1]);
}
const { data } = await axios.post('/api/upload', formData, {
headers: { 'Content-Type': 'multipart/form-data' }
})
} catch (ex) {
console.error(ex)
}
}
The call goes through page/api/middleware/middleware.js
import nextConnect from 'next-connect'
import multiparty from 'multiparty'
const middleware = nextConnect()
middleware.use((req, res, next) => {
const form = new multiparty.Form()
form.parse(req, function (err, fields, files) {
if (err) {
console.log(err)
next()
}
req.body = fields
req.files = files
next()
})
})
export default middleware
And then is passed to the handler in ./page/api/upload.js.
handler.post(async function handlePost ({ body, files }, response) {
try {
const fileUrl = await uploadFileToIPFS(files.file[0]) //
const metadata = {
name: body.name[0],
description: body.description[0],
image: fileUrl
}
const metadaUrl = await uploadJsonToIPFS(metadata, body.name[0])
return response.status(200).json({
url: metadaUrl
})
} catch (error) {
console.log('Error uploading file: ', error)
}
})
However, i can't retrieve the files here and get this error Error uploading file: TypeError: Cannot read properties of undefined (reading '0').
console.log(body, files) here gives: {"undefined":["[object Promise]"]} {}`.
Why can't I retrieve the formdata here?

Related

use state to build url query string

I am new to Redux, so any help would be appreciated.
I want to add a variable to my fetch GET request URL inside the action creator.
yourapi.com/getuser/{user1}
I might not be following the correct process, I am very new to redux. I am using NextJS with React-Redux for this project.
My action:
// Get User Object
export const load_user = () => async dispatch => {
try {
const res = await fetch(`/api/getuser`, {
method: 'GET',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
}
});
const data = await res.json();
if (res.status === 200) {
dispatch({
type: LOAD_USER_SUCCESS,
payload: data
});
} else {
dispatch({
type: LOAD_USER_FAIL
});
}
} catch(err) {
dispatch({
type: LOAD_USER_FAIL
});
}
};
That part seems ok.
In this getuser.js file, the action calls (The action creator) how do I append a username variable onto the URL ${API_URL}/GetUser/{username} ?
export default async (req, res) => {
if (req.method === 'GET') {
const username = ??????????
try {
// How to get username???
const apiRes = await fetch(`${API_URL}/GetUser/username`, {
method: 'GET',
headers: {
'Accept': 'application/json',
}
});
const data = await apiRes.json();
if (apiRes.status === 200) {
return res.status(200).json({
user: data
});
} else {
return res.status(apiRes.status).json({
error: data.error
});
}
} catch(err) {
return res.status(500).json({
error: 'Something went wrong when retrieving user'
});
}
} else {
// Error. Not a GET request. They tried POST or PUT etc.
res.setHeader('Allow', ['GET']);
return res.status(405).json({
error: `Method ${req.method} not allowed`
});
}
};
I tried
const user = useSelector(state => state.user)
but I get the error
Invalid hook call error - TypeError: Cannot read properties of null (reading 'useContext')

NEXT.JS – Using aws-sdk to upload to DigitalOceans - dev mode work, prod isn't

I'm using aws-sdk to upload images to DigitalOceans bucket. On localhost it works 100% but production seems like the function goes on without an error but the file does not upload to the bucket.
I cannot figure out what is going on and can't think of a way to debug this. tried aswell executing the POST request with Postman multipart/form-data + adding file to the body of the request and it is the same for localhost, working, and production is not.
my api endpoint:
import AWS from 'aws-sdk'
import formidable from "formidable"
import fs from 'fs'
const s3Client = new AWS.S3({
endpoint: process.env.DO_SPACES_URL,
region: 'fra1',
credentials: {
accessKeyId: process.env.DO_SPACES_KEY,
secretAccessKey: process.env.DO_SPACES_SECRET
}
})
export const config = {
api: {
bodyParser: false
}
}
export default async function uploadFile(req, res) {
const { method } = req
const form = formidable()
const now = new Date()
const fileGenericName = `${now.getTime()}`
const allowedFileTypes = ['jpg', 'jpeg', 'png', 'webp']
switch (method) {
case "POST":
try {
form.parse(req, async (err, fields, files) => {
const fileType = files.file?.originalFilename?.split('.').pop().toLowerCase()
if (!files.file) {
return res.status(400).json({
status: 400,
message: 'no files'
})
}
if (allowedFileTypes.indexOf(fileType) === -1) {
return res.status(400).json({
message: 'bad file type'
})
}
const fileName = `${fileGenericName}.${fileType}`
try {
s3Client.putObject({
Bucket: process.env.DO_SPACES_BUCKET,
Key: `${fileName}`,
Body: fs.createReadStream(files.file.filepath),
ACL: "public-read"
}, (err, data) => {
console.log(err)
console.log(data)
})
const url = `${process.env.FILE_URL}/${fileName}`
return res.status(200).json({ url })
} catch (error) {
console.log(error)
throw new Error('Error Occured While Uploading File')
}
});
return res.status(200)
} catch (error) {
console.log(error)
return res.status(500).end()
}
default:
return res.status(405).end('Method is not allowed')
}
}

What's the proper way for returning a response using Formidable on Nextjs Api?

I'm sending an uploaded file to a Next.js API route using FormData. The file is then processed on the API route using formidable and passed to sanity client in order to upload the asset, but I can't return the data to the client... I get this message in console:
API resolved without sending a response for /api/posts/uploadImage, this may result in stalled requests.
When console logging the document inside the API everything is in there, I just can't send back that response to client side. Here's my client upload function:
const addPostImage = (e) => {
const selectedFile = e.target.files[0];
if (
selectedFile.type === "image/jpeg" ||
selectedFile.type === "image/png" ||
selectedFile.type === "image/svg" ||
selectedFile.type === "image/gif" ||
selectedFile.type === "image/tiff"
) {
const form = new FormData();
form.append("uploadedFile", selectedFile);
axios
.post("/api/posts/uploadImage", form, {
headers: { "Content-Type": "multipart/form-data" },
})
.then((image) => {
setPostImage(image);
toast.success("Image uploaded!");
})
.catch((error) => {
toast.error(`Error uploading image ${error.message}`);
});
} else {
setWrongImageType(true);
}
};
This is my API:
import { client } from "../../../client/client";
import formidable from "formidable";
import { createReadStream } from "fs";
export const config = {
api: {
bodyParser: false,
},
};
export default async (req, res) => {
const form = new formidable.IncomingForm();
form.keepExtensions = true;
form.parse(req, async (err, fields, files) => {
const file = files.uploadedFile;
const document = await client.assets.upload(
"image",
createReadStream(file.filepath),
{
contentType: file.mimetype,
filename: file.originalFilename,
}
);
console.log(document);
res.status(200).json(document);
});
};
Solution:
As stated in the comments by #juliomalves, I had to promisify the form parsing function and await its results like so:
import { client } from "../../../client/client";
import formidable from "formidable";
import { createReadStream } from "fs";
export const config = {
api: {
bodyParser: false,
},
};
export default async (req, res) => {
const form = new formidable.IncomingForm();
form.keepExtensions = true;
const formPromise = await new Promise((resolve, reject) => {
form.parse(req, async (err, fields, files) => {
if (err) reject(err);
const file = files.uploadedFile;
const document = await client.assets.upload(
"image",
createReadStream(file.filepath),
{
contentType: file.mimetype,
filename: file.originalFilename,
}
);
resolve(document);
});
});
res.json(formPromise);
};
Then I checked for the response's status on the client-side.
Your code is not working because by default formidable saves files to disk, which is not available on vercel. This works.
const chunks = []
let buffer;
const form = formidable({
fileWriteStreamHandler: (/* file */) => {
const writable = new Writable();
// eslint-disable-next-line no-underscore-dangle
writable._write = (chunk, enc, next) => {
chunks.push(chunk);
next();
};
return writable;
},
})
form.parse(req, (err, fields) => {
if (err) {
res.end(String(err));
return;
}
buffer = Buffer.concat(chunks);
res.end();
});

how to Upload a file from ue4 to Js server, using multer

I'm currently making a project that requires me to send a png image from unreal engine to a next JS server which uses multer to pass the file on to another server.
When sending my file as a binary the JS server (intermediate server) is not receiving a file from unreal.
I've tried the two following methods
TArray<uint8> rawFileData;
FFileHelper::LoadFileToArray(rawFileData, *media);
Request->SetURL(API_HP_URL + "nude_upload");
Request->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=----WebKitFormBoundarywpp9S2IUDici8hpI"));
Request->SetHeader(TEXT("Connection"), TEXT("keep-alive"));
Request->SetHeader(TEXT("accept"), TEXT("application/json, text/plain, */*"));
Request->SetContent(rawFileData);
Request->SetVerb("POST");
Request->OnProcessRequestComplete().BindUObject(this, &AHttpCommunicator::OnPostNudeSSResponse);
Request->ProcessRequest();
and
FString JsonString;
TArray<uint8> rawFileData;
TSharedRef<TJsonWriter<TCHAR>> JsonWriter = JsonWriterFactory<TCHAR>::Create(&JsonString);
JsonWriter->WriteObjectStart();
JsonWriter->WriteValue("fileName", pPathToFile);
JsonWriter->WriteValue("file", FBase64::Encode(rawFileData));
JsonWriter->WriteObjectEnd();
JsonWriter->Close();
Request->SetURL(API_HP_URL + "nude_upload");
Request->SetHeader(TEXT("Content-Type"), TEXT("multipart/form-data; boundary=----WebKitFormBoundarywpp9S2IUDici8hpI"));
Request->SetHeader(TEXT("Connection"), TEXT("keep-alive"));
Request->SetHeader(TEXT("accept"), TEXT("application/json, text/plain, */*"));
Request->SetContentAsString(JsonString);
Request->SetVerb("POST");
Request->OnProcessRequestComplete().BindUObject(this, &AHttpCommunicator::OnPostNudeSSResponse);
Request->ProcessRequest();
both of these methods have the server return an undefined file obj
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import path from 'path';
import MulterGoogleCloudStorage from "multer-google-storage";
import nextConnect from 'next-connect';
const Multer = require('multer');
const { Storage } = require('#google-cloud/storage');
const CLOUD_BUCKET = 'nude_locks';
const PROJECT_ID = 'hp-production-338902';
const KEY_FILE = path.resolve('./hp-production-key.json')
const storage = new Storage({
projectId: PROJECT_ID,
keyFilename: KEY_FILE
});
const bucket = storage.bucket(CLOUD_BUCKET);
const upload = Multer({
storage: Multer.memoryStorage(),
limits: {
fileSize: 5 * 1024 * 1024,
}
}).single('file');
const apiRoute = nextConnect({
onNoMatch(req, res) {
res.status(405).json({ error: `Method '${req.method}' Not Allowed` });
},
});
apiRoute.use(upload);
apiRoute.post((req, res) => {
console.log(req.file);
if (!req.file) {
res.status(400).send("No file uploaded.");
return;
}
const blob = bucket.file(req.file.originalname);
// Make sure to set the contentType metadata for the browser to be able
// to render the image instead of downloading the file (default behavior)
const blobStream = blob.createWriteStream({
metadata: {
contentType: req.file.mimetype
}
});
blobStream.on("error", err => {
next(err);
return;
});
blobStream.on("finish", () => {
console.log('finish');
console.log(blob);
// The public URL can be used to directly access the file via HTTP.
const publicUrl = `https://storage.googleapis.com/${bucket.name}/${blob.name}`;
// Make the image public to the web (since we'll be displaying it in browser)
blob.makePublic().then(() => {
res.status(200).send(`Success!\n Image uploaded to ${publicUrl}`);
});
});
blobStream.end(req.file.buffer);
});
export default apiRoute;
export const config = {
api: {
bodyParser: false,
},
}
const fileSelectedHandler = e => {
const file = new File("D:/_Spectre/VHS/P210107_VHS_Configurator/P04_Unreal/Human_Configurator/Saved/Screenshots/Windows/-1-nude-2022-2-13.png");
console.log(file);
const formData = new FormData();
formData.append('file', file);
axios.post('/api/nude_upload', formData, {
headers: {
'Content-Type': 'multipart/form-data',
}
})
.then(res => {
console.log(res);
});
}
Is there a way to create a file object from UE4?
alternatively is there a way to retrieve google cloud storage access tokens from UE4

Savings a DocRaptor response to Firebase storage inside a callable Firebase Function

I am trying to send a template to DocRaptor and then get a pdf back and save that in a Firebase Storage location. My issue is that DocRaptor returns binary data for the PDF rather than something like a link to the pdf. I'm trying to convert this to a buffer to save in storage, however, when it saves the file, it saves it as application/octet-stream type instead of application/pdf.
/* DOCRAPTOR PDF GENERATOR */
export const createPdfFile = functions.https.onCall((data, context) => {
const config = {
url: 'https://docraptor.com/docs',
encoding: null,
headers: {
'Content-Type' : 'application/json'
},
json: {
user_credentials: docRaptorKey,
doc: {
document_content: data.content,
type: "pdf",
test: sandboxMode
}
}
}
return new Promise((resolve, reject) => {
requestModule.post(config, (err, response, body) => {
if(!err) {
let buffer = new Buffer(body, "binary");
bucket.file(data.path).save(buffer).then((results) => {
resolve(response);
}).catch((error) => {
reject(error);
});
} else {
reject(err);
}
});
}).then((response) => {
return {'results':response}
}).catch((error) => {
throw new functions.https.HttpsError('unknown', error);
});
});

Resources