I am trying to use the Piniata api. Here it is:
https://docs.pinata.cloud/
The idea is to upload and pin and image using the api, into my account in Piniata.
I got this sample to upload a file in base64, using Node.js and server side.
The sample use this api call:
"https://api.pinata.cloud/pinning/pinFileToIPFS"
I am suppose to be able to do this in client side as well.
However, there is no sample of client side without using Node.js. And I can't seem to find exactly a documentation of what the api call expects.
Here is the sample I got from the Piniata support:
const { Readable } = require("stream");
const FormData = require("form-data");
const axios = require("axios");
(async () => {
try {
const base64 = "BASE64 FILE STRING";
const imgBuffer = Buffer.from(base64, "base64");
const stream = Readable.from(imgBuffer);
const data = new FormData();
data.append('file', stream, {
filepath: 'FILENAME.png'
})
const res = await axios.post("https://api.pinata.cloud/pinning/pinFileToIPFS", data, {
maxBodyLength: "Infinity",
headers: {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey
}
});
console.log(res.data);
} catch (error) {
console.log(error);
}
})();
Here is my attempt to perform an upload from client side without Node.js
async function uploadFile(base64Data)
{
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
var status = 0;
try {
let data = new FormData();
var fileName = "FILENAME.png";
var file = new File([base64Data], fileName, {type: "image/png+base64"});
data.append(`data`, file, file.name);
data.append(`maxBodyLength`, "Infinity");
const response = await postData('POST', url, {
'Content-Type': `multipart/form-data; boundary=${data._boundary}`,
"Authorization": "Bearer Redacted"
},
data
);
} catch (error) {
console.log('error');
console.log(error);
}
}
What I get as a response from the server is 400 and the error being:
{"error":"Invalid request format."}
What am I doing wrong?
Also, it seems like when I try to use FormData .append with a stream as a sample, it doesn't work. As if it only expects a blob.
Related
I am working on a project (using React.js Express.js and Node.js) to convert a user uploaded image into and NFT on Ethereum blockchain and for that, I need to upload the image on an IPFS (I am using Pinata) and then use the pinata URI in the metadata to mint a new NFT. (Do let me know if I am wrong here, I am still newbie to web3)
For uploading my image onto the Pinata IPFS, I am sending the base64 string of the image from the client side to the server side and then calling the pinFileToIPFS method. This is the code of my server side file
const axios = require('axios');
const fs = require('fs');
const FormData = require('form-data');
const router = require('express').Router();
const { Readable } = require('stream');
const pinFileToIPFS = (image) => {
const url = `https://api.pinata.cloud/pinning/pinJSONToIPFS`;
const buffer = Buffer.from(image);
const stream = Readable.from(buffer);
const filename = `an-awesome-nft_${Date.now()}.png`;
stream.path = filename;
const formData = new FormData();
formData.append("file", stream);
return axios.post(url,
formData,
{
headers: {
'Content-Type': `multipart/form-data; boundary= ${formData._boundary}`,
'pinata_api_key': "*******************",
'pinata_secret_api_key': "**********************************",
}
}
).then(function (response) {
console.log("Success: ", response);
}).catch(function (error) {
console.log("Fail! ", error.response.data);
});
};
router.route('/').post((req, res) => {
const image = req.body.image;
pinFileToIPFS(image);
});
module.exports = router;
Here req.body.image contains the base64 string of the user uploaded file.
I have tried to convert the base64 string into a buffer and then convert the buffer into a readable stream (as done in the official Pianata documentation but for a localy file) and then wrap it up in FormData(), but I keep getting the following error.
data: {
error: 'This API endpoint requires valid JSON, and a JSON content-type'
}
I know the problem is with the format my image/file is being sent to the API but I can't figure out. I am still a newbie to web3 and blockchains so please help!
The recommended way of interacting with Pinata, is by using their Node.JS SDK. This SDK has a pinFileToIPFS function, allows you to upload an image to their IPFS nodes in the form of a readableStream.
A sample of this would look like
const fs = require('fs');
const readableStreamForFile = fs.createReadStream('./yourfile.png');
const options = {
pinataMetadata: {
name: MyCustomName,
keyvalues: {
customKey: 'customValue',
customKey2: 'customValue2'
}
},
pinataOptions: {
cidVersion: 0
}
};
pinata.pinFileToIPFS(readableStreamForFile, options).then((result) => {
//handle results here
console.log(result);
}).catch((err) => {
//handle error here
console.log(err);
});
However, if you are deadset on using their API endpoints and simply posting to them via axios, there is a seperate API endpoint. /pinning/pinFileToIPFS. Examples of this method can be found in their API Docs.
You may want to consider changing the following two lines and using the https://api.pinata.cloud/pinning/pinFileToIPFS endpoint instead:
const buffer = Buffer.from(image); -> const buffer = Buffer.from(image, "base64");
and
formData.append("file", stream); -> formData.append("file", stream, "fileNameOfChoiche.png);
When you are uploading an image or file to Pinata IPFS with node js. These are the steps that even don't need Pinata Node.js SDK.
1- You can upload an image from the front end with React or Next.js. Code is given below.
const uploadAttachment = async (data, token) => {
try {
return await Api.post(`${ApiRoutes.upload_attachment}`, data, {
headers: {
Authorization: "Bearer " + token, //the token is a variable which holds the token
},
});
} catch (error) {
return {
status: 404,
};
}
};
export default uploadAttachment;
2- You need to install multer to upload an image.
const multer = require("multer");
global.uploadSingleFile = multer({ dest: "uploads/" });
3- Set up your route with multer middleware and action which you are going to call.
.post(
"/attachments/upload",
uploadSingleFile.single("file"),
actions.attachments.upload.pinFileToIPFSLocal
);
4- Last step where you will hit the Pinata endpoint with Pinata API & Secret key.
pinFileToIPFSLocal: async (req, res, next) => {
try {
const url = "https://api.pinata.cloud/pinning/pinFileToIPFS";
let formData = new FormData();
formData.append("file", JSON.stringify(req.file), req.file.originalname);
axios
.post(url, formData, {
maxContentLength: -1,
headers: {
"Content-Type": `multipart/form-data; boundary=${formData._boundary}`,
pinata_api_key: process.env.PINATA_API_KEY,
pinata_secret_api_key: process.env.PINATA_API_SECRET,
path: "somename",
},
})
.then((data) => {
console.log("Result...", data);
return utils.response.response(
res,
"Upload image to ipfs.",
true,
200,
data.data
);
})
.catch((err) => {
return utils.response.response(
res,
"Image not upload to ipfs",
false,
200,
err
);
});
} catch (error) {
next(error);
}
The error message is clear. You are using url that used for json file upload. this is the url you should use to upload image
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
you don't have to convert buffer to a readable stream.
I am not sure if this is correct ${formData._boundary}. should be
"Content-Type": `multipart/form-data: boundary=${formData.getBoundary()}`,
There must be an error on the image parameter. A simple buffer representation of the image should work. The readable stream is not necessary.
Instead of creating the buffer, you could use middleware like express-fileupload to access the buffer representation of the file uploaded on the client-side directly.
const file = req.files;
const url = "https://api.pinata.cloud/pinning/pinFileToIPFS";
const data = new FormData();
data.append("file", file.file.data, { filepath: "anyname" });
const result = await axios.post(url, data, {
maxContentLength: -1,
headers: {
"Content-Type": `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: process.env.PINATA_API_KEY,
pinata_secret_api_key: process.env.PINATA_API_SECRET,
path: "somename",
},
});
I have some issues parsing a request made from the front-end using FormData. This is the example request generated from Postman for node.js Axios. If I use the postman app with the request, it works as expected.
Frontend example generated from Postman code feature.
var axios = require('axios');
var FormData = require('form-data');
var fs = require('fs');
var data = new FormData();
data.append('file', fs.createReadStream('/some_file.jpg')); **//I am using Electron and I have acces to the FileSystem from the client.**
data.append('resizeLargeImage[width]', '1920');
data.append('resizeLargeImage[height]', '1080');
data.append('resizeLargeImage[type]', 'cover');
var config = {
method: 'post',
url: 'localhost:3030/api/v1/optimize-single',
headers: {
'x-api-key': '123',
...data.getHeaders()
},
data : data
};
axios(config)
.then(function (response) {
console.log(JSON.stringify(response.data));
})
.catch(function (error) {
console.log(error);
});
Backend
#Post('/optimize-single')
#UseInterceptors(FileInterceptor('file'))
async uploadFile(
#UploadedFile() file: FileDto,
#Body() body: UploadFileParametersDto,
#Res() response: Response,
): Promise<any> {
console.log('file', file, 'body', body);
**//File is undefined, body is a null Object**
return await this.appService.uploadFile(file, body, response);
}
Any ideas as to why Nest doesn't recognize this type of request?
Thanks!
I managed to figure it out.
If you are in the Renderer process, it works by attaching the file as a blob.
const fileBuffer = fs.readFileSync(filePath);
const fileName = path.basename(filePath);
const blob = new Blob([fileBuffer], {
type: mime.lookup(filePath),
});
formData.append('file', blob, fileName);
If you move the same functionality in the Main process it will work as expected with data.append('file', fs.createReadStream('/some_file.jpg'));
I have a test that should read a image file and submit the image file to an api that accepts a multipart-formdata.
I am using the fetch api along with formdata class to set the image file. The formdata only accepts a blob. So in my test i must convert the the file i read in which is of type stream to a blob.
test("should submit front document", async () => {
const side = "front";
const stream = fs.createReadStream(process.cwd() + "/test/resources/" + "id/front.jpg");
const image = await streamToBlob(stream);
const front = await myLibrary.document(id, side, image);
expect(front.success).toBe(true);
});
I am attempting to use a library here to convert the stream to a blob https://www.npmjs.com/package/stream-to-blob. However the test is failing. If i attempt to console.log(image) i get the following Blob {}
Why is the blob empty {}?
async document(id, side, image) {
const url = this.API_URL + "/document"
let formData = new FormData();
formData.set("image", image, "front.jpg");
formData.set("side", side);
let headers = new Headers();
headers.set("Authorization", "Bearer " + this.API_KEY);
const request = {
method: "POST",
body: formData,
headers: headers,
};
try {
const response = await fetch(url, request);
const data = await response.json();
return data;
} catch (err) {
throw err;
}
}
UPDATED with res.send(data) instead of res.json(data)
Using Angular 6 and NodeJS I am doing a web application.
I am trying to download a file from a http post request.
I send a request to the server like this. From my component I call a function in a service. In the component, I susbscribe to have the answer of the server and when I have it I create a new Blob with the response and I Use FileSaver to download the pdf.
Now, when I received the answer from the server, the client sees it like an error whereas the status is 200. The error message is:
"Http failure during parsing for http://localhost:3000/api/experiment/regression"
See the screenshot below.
Component.ts
this.api.postML(this.regression).subscribe(
res => {
console.log(res);
let pdf = new Blob(res.data, { type: "application/pdf" });
let filename = "test.pdf";
FileSaver.saveAs(pdf, filename);
},
err => {
alert("Error to perform the regression");
console.log(err);
}
);
API.Service.ts
public postML(data): Observable<any> {
// Create url
let url = `${baseUrl}${"experiment/regression"}`;
let options = {
headers: { "Content-Type": "application/json", Accept: "application/pdf" }
};
// Call the http POST
return this.http
.post(url, data, options)
.pipe(catchError(this.handleError));
}
Then from the server, it executes some code with the data sent and generates a PDF file.
Then, I would like to send the pdf as a response to the client.
I tried like this:
fs.readFile("/home/user/test.pdf", function(err, data) {
let pdfName = "Report.pdf";
res.contentType("application/pdf");
res.set("Content-Disposition", pdfName);
res.set("Content-Transfer-Encoding", "binary");
console.log(data);
console.log("Send data");
res.status(200);
res.send(data);
});
In the client, I have the answer. The console log is:
Finally, I found a video tutorial and it was very basic.
Node.js Server:
const express = require("express");
const router = express.Router();
router.post("/experiment/resultML/downloadReport",downloadReport);
const downloadReport = function(req, res) {
res.sendFile(req.body.filename);
};
Angular Component:
import { saveAs } from "file-saver";
...
download() {
let filename = "/Path/to/your/report.pdf";
this.api.downloadReport(filename).subscribe(
data => {
saveAs(data, filename);
},
err => {
alert("Problem while downloading the file.");
console.error(err);
}
);
}
Angular Service:
public downloadReport(file): Observable<any> {
// Create url
let url = `${baseUrl}${"/experiment/resultML/downloadReport"}`;
var body = { filename: file };
return this.http.post(url, body, {
responseType: "blob",
headers: new HttpHeaders().append("Content-Type", "application/json")
});
}
I'm trying to upload an image to slack using the slack api https://api.slack.com/methods/files.upload.
function uploadImage(file) {
var form = new FormData();
form.append('image', file);
var self = this;
const config = {
headers: {
'content-type': 'multipart/form-data'
}
}
var token = 'myToken'
axios.post('https://slack.com/api/files.upload?token=' + token, form, config)
.then(function (response) {
console.log(response);
self.imageUploaded("Image uploaded")
})
.catch(function (error) {
console.log(error);
self.error(error);
})
}
I'm getting response with "invalid form data". Any idea what might be going wrong?
Thanks and best regards!
PS: I was able to post an image to slack with python
def post_image(filename, token, channels):
f = {'file': (filename, open(filename, 'rb'))}
response = requests.post(url='https://slack.com/api/files.upload',
data={'token': token, 'channels': channels},
headers={'Accept': 'application/json'},
files=f)
return response.tex
Just need to make the same request with axioms
you can upload a file using axios with the form-data lib
const fs = require("fs");
const axios = require("axios");
const FormData = require("form-data");
const form = new FormData();
form.append("token", 'token here');
form.append("channels", channelId);
form.append("file", fs.createReadStream(__dirname + "/aapl.png"), 'file name');
try {
const res = await axios.post("https://slack.com/api/files.upload", form, {
headers: form.getHeaders(),
});
} catch (err) {
throw new Error(err);
}