Turn base64 string into png for upload to s3 in frontend - javascript

I feel like I'm going crazy, I really hope this is not a duplication although I did find multiple similar issues but none of the solutions worked for me.
I'm using react-signature-canvas to let people sign on the website. That library offers a toDataURL() method, which turns the canvas into a base64 string. I then remove the "data:image/png;base64," from that string and turn it into a Blop using the fetch function. Getting a presigned URL I use axios.put() to send the image to an S3 bucket. However when I download that image from that bucket I can't open it because it's corrupted. When I put the dataURL into a base64 to image converter online the string works.
Here is my code so far:
const fileUpload = async signatureImageSrc => {
const signatureImage = await (await fetch(signatureImageSrc)).blob();
const fileBody = new FormData();
fileBody.append("signature", signatureImage);
const config = {
headers: {
"Content-Type": "application/json"
}
};
const fileName = id + "-" + "signature.png";
axios
.post("/upload_url", {fileName: fileName, fileType: "image/png"}, config)
.then(res => {
axios.put(res.data, fileBody, config).then(res => console.log(res));
});
};
I have tried changing the type of the blop (because it currently set to text/html) as well as sending it appended to a Form data object, as well as changing the Content-Type in the config object. I tried creating it as a file ( new File([blop], fileName)) and sending it through and more.
It always gets corrupted or sometimes it's a .json or .html file.

I finally got it to work.
I used a function to create a blob from the base64 string and then turned that blob into a file by adding the lastModifiedDate and name property to it.
Additionally I did not use a form data element anymore but instead uploaded that created file directly to the S3 bucket.
The code looks like this now:
const fileUpload = signatureImageSrc => {
const config = {
headers: {
"Content-Type": "application/json"
}
};
const configBlop = {
headers: {
"Content-Type": "multipart/form-data"
}
};
const blob = b64toBlob(signatureImageSrc, "image/png");
const fileName = id + "-" + "beauftragung-unterschrift.png";
const file = blobToFile(blob, fileName);
axios
.post("/upload_url", {fileName: fileName, fileType: "image/png"}, config)
.then(res => {
axios.put(res.data, file, configBlop).then(res => console.log(res));
});
};
The function to create the Blob is taken from here and the blobToFile function looks like this:
const blobToFile = (theBlob, fileName) => {
theBlob.lastModifiedDate = new Date();
theBlob.name = fileName;
return theBlob;
};

Related

How do you user-inputted folders?

I'm wanting to allow users to select a folder(containing a .glb file) from their device, and then upload it to IPFS. The problem is that all documentation that I've seen for using a post method requires that you pass a local directory into the FormData (for example):
const pin = async (pinataApiKey, pinataSecretApiKey) => {
const url = `https://api.pinata.cloud/pinning/pinFileToIPFS`;
const src = "LOCAL/FILE/PATH";
var status = 0;
try {
const { dirs, files } = await rfs.read(src);
let data = new FormData();
for (const file of files) {
data.append(`file`, fs.createReadStream(file), {
filepath: basePathConverter(src, file),
});
}
return axios.post(url, data, {
maxBodyLength: "Infinity",
headers: {
"Content-Type": `multipart/form-data; boundary=${data._boundary}`,
pinata_api_key: pinataApiKey,
pinata_secret_api_key: pinataSecretApiKey,
},
});
} catch (error) {}
};
My question is, how do I get a user-inputted folder, then handle it so that it can be uploaded to Pinata IPFS through a POST method? I'm aware of <input type= "file" webkitdirectory>, but I'm not sure how to extract and "prep" the folder data for posting once a user has uploaded it. Any suggestions are welcome!

Slack API upload string as file

I have a sting variable of a csv. I want to upload it to a slack channel as a .csv file, not as text.
async function run() {
const csvData = 'foo,bar,baz';
const url = 'https://slack.com/api/files.upload';
const res = await axios.post(url, {
channel: '#csvchannel',
filename: 'CSVTest.csv',
content: csvData
}, { headers: { authorization: `Bearer ${slackToken}` } });
console.log('Done', res.data);
}
This code returns: error: 'no_file_data', Changing content to file gives the same response.
What do I have to do to convert the csv sting into a file that can be uploaded? I can't use fs to write out the file.
I have tried to use fs.createReadStream(csvData) but that needs a file, not a string.
Slack API documentation: https://api.slack.com/methods/files.upload
You don't need to convert the CSV into a file, seems you are missing a couple of things here:
fileType property, it needs to be CSV.
Slack file upload API supports multipart/form-data and
application/x-www-form-urlencoded content types.
You're missing the Content-Type.
Check out a working example of how you could send the data using application/x-www-form-urlencoded
Send a CSV to Slack                                                                                
View in Fusebit
const csvData = 'foo,bar,baz';
const url = 'https://slack.com/api/files.upload';
const params = new URLSearchParams()
params.append('channels', slackUserId);
params.append('content', csvData);
params.append('title', 'CSVTest');
params.append('filetype', 'csv');
const result = await axios.post(url, params,
{
headers:
{
authorization: `Bearer ${access_token}`,
'Content-Type': 'application/x-www-form-urlencoded'
}
});
ctx.body = { message: `Successfully sent a CSV file to Slack user ${slackUserId}!` };

Pinata IPFS's pinFileToIPFS method not accepting a user uploaded file

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",
},
});

Getting PDF from Json API response

I have a problem with the PDF file I get from the API in response to a Json GET request.
It does get a good string in Json, however, which makes the PDF file that appears corrupted. I tried to convert the response to a string but it didn't do anything.
Here is my code:
getPDF() {
axios
.get(apiurl + "api/Dok", { params: { }, headers: { } })
.then(response => {
const res = response.data.fileData;
const pdfcode = res.toString();
this.convertPDF(pdfcode)
}
)
.catch(error => {
alert('error')
});
}
convertPDF(value) {
const file = new Blob(
[value],
{type: 'application/pdf'});
const fileURL = URL.createObjectURL(file);
window.open(fileURL);
}
So if i'm import the pdf file from local and add it to this function istead of url in console i'm getting long string response
JVBERi0xLjUNCjQgMCBvYmoNCjw8L1R5cGUgL1BhZ2UvUGFyZW50IDMgMCBSL0NvbnRlbnRzIDUgMCBSL01lZGlhQ...
and PDF works, but when get it from URL i'm getting :
{"fileData":"JVBERi0xLjUNCjQgMCBvYmoNCjw8L1R5cGUgL1BhZ2UvUGFyZW50...
I checked it in notepad, the first answer and the content of "" are identical
any ideas what i can do?

How to retrieve an Image from img tag with remote url and create File object from it

Steps:
I have a remote image url.
I generated the img tag from it.
Now I want to read this img tag and convert it into a File object so that I can send it to server for upload.
Method 1 Tried: I have already tried the fetch method and directly tried to fetch image data from remote url.
Method 2 Tried:
clicked = () => {
const img = document.getElementById('someId');
const options = {
method: 'get',
headers: {
'Access-Control-Request-Headers': '*',
'Access-Control-Request-Method': '*',
},
mode: 'cors',
};
fetch(img.src, options)
.then(res => res.blob())
.then((blob) => {
const file = new File([blob], 'dot.png', blob);
console.log(file);
});
}
Expected output is File object.
I am using axios here but the principal should be the same:
const res = await axios.get(`/api/image/${imageId}`, {
responseType: 'arraybuffer'
});
const imgFile = new Blob([res.data]);
const imgUrl = URL.createObjectURL(imgFile);
Ignore the response type.

Categories