My React application captures the webcam image in data uri format (e.g. data:image/jpeg;base64, iVBOR...). I then send the image in this form to my API in Node.js from where I use Axios to send it to the Microsoft Azure Detect API. However, I get an error
error: {
code: 'InvalidImage',
message: 'Decoding error, image format unsupported.'
}
My Node.js code:
app.post('/detect', async (req, res) => {
const dataUrl = req.body.imageDataUrl;
if (dataUrl.length === 0) return;
axios.post(
url,
{url: dataUrl},
{
timeout: 50000,
headers: {
'Ocp-Apim-Subscription-Key': subscriptionKey,
'Content-Type': 'application/octet-stream',
},
params: {
returnFaceId: false,
returnFaceLandmarks: false,
returnFaceAttributes: faceAttributes,
detectionModel: detectionModel,
},
}
);
});
I tried to convert a data uri to a blob using this function
function dataURItoBlob(dataURI) {
var data = dataURI.split(',')[1];
var byteString = Buffer.from(data, 'base64');
var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0];
var blob = new Blob([byteString], {type: mimeString});
return blob;
};
but then the error message looks
error: { code: 'InvalidImageSize', message: 'Image size is too small.' }
even though the size of the captured image is 1280x720
Here you are setting the content type toapplication/octet-stream but you are passing a string i.e., your URL so you have to set it to application/json
Then you can create a Json object which can passed which is of following format.
{
"url":"<Your Image URL >"
}
Here I tried have tried this in postman
Headers:
Params:
Body:
Output :
Refer this MSDOC on detect api.
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",
},
});
Hello i'm having issue with uploading image to the cloud (Backblaze B2).
The problem is, when I use the example Thunder client to upload the file everything works fine and file is shown.
Now my problem is that when I upload with JS I don't know why it is corrupted or bugged.
Like when I upload an image and download it, Windows File Manager says : file format not supported.
I decoded the file with base64 img decoder and it works fine and image is shown.
const submitForm = () => {
var reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = function(e) {
// binary data
let imagedata = reader.result.slice(reader.result.indexOf(',') + 1)
console.log(imagedata);
console.log(sha1(imagedata));
const proxyurl = "https://cors-anywhere.herokuapp.com/";
let datadf = fetch(proxyurl + 'url', {
method: 'POST',
headers: {
"Accept": "*/*",
"User-Agent": "Thunder Client (https://www.thunderclient.io)",
"X-Bz-File-Name": file.name,
"X-Bz-Content-Sha1": "do_not_verify",
"Authorization": "auth",
"Content-Type": "b2/x-auto",
},
body: imagedata,
})
.then((response) => {
return response.json();
})
.catch((err) => {
return {
status: 'fail',
message: 'API CALL ERROR',
error: err.message
};
});
datadf.then(res => console.log(res))
};
reader.onerror = function(e) {
// error occurred
console.log('Error : ' + e.type);
};
.readAsDataURL() converts the file it reads into Base64, so it can be represented as a URL you can put into a browser. A very long URL, but still a URL.
If you store a Base 64 representation of an image into a file on your machine, then try to read it with an image-display program, the operation will fail: "This doesn't look like a .jpg, .png, or .gif" so I don't know what to do with it." That's what your Windows file manager error message means.
If you want the file's contents raw rather than Base64 encoded, you'll need to use .readAsArrayBuffer().
I've been trying to create a file using the Google APIs for Browser. I reused some of the code that I used to communicate with the api from NodeJS in the past and repurposed it for the browser.
const content = "this is some content";
const fileMetadata = {
name: "my-file.txt",
alt: "media",
};
const media = {
mimeType: "text/plain",
body: content,
};
const {
result: { id: fileId },
} = await gapi.client.drive.files.create({
resource: fileMetadata,
media: media,
fields: "id",
});
I typically get a successful response saying that my file was created.
However, when I try to get the contents of the file the body field is an empty string.
const { body } = await gapi.client.drive.files.get({
fileId: fileId,
});
console.log(body)
// ""
I think the request to create the file might not be formatted correctly but it works when it runs on the backend so I'm confused as to why it doesn't work in the browser.
You are mentioning that you are using the Google APIs for browser, and not node.js.
I would recommend to send the request directly against the Google REST API, as gapi.client.drive.create() appears to have problems sending the actual binary file (while sending metadata appears to work). Look here, for example: https://stackoverflow.com/a/53841879/7821823, https://stackoverflow.com/a/35182924/7821823 or https://stackoverflow.com/a/68595887/7821823
You can send the data as a blob and create the request using the FormData class.
async upload(blob, name, mimeType, parents = ["root"]) {
const metadata = { name, mimeType, parents };
const form = new FormData();
form.append("metadata", new Blob([JSON.stringify(metadata)], { type: "application/json" }));
form.append("file", blob);
return fetch("https://www.googleapis.com/upload/drive/v3/files?uploadType=multipart&supportsAllDrives=true", {
method: "POST",
headers: new Headers({ Authorization: `Bearer ${gapi.auth.getToken().access_token}` }),
body: form,
});
}
I have not tested if you can send a String instead of a Blob, but you can easily create a Blob from a String:
const content = "this is some content";
const blob = new Blob([content], { type: 'text/plain' });
The body of the file should be in the form of a stream. Not plain text.
var media = {
mimeType: 'text/plain',
body: fs.createReadStream('files/my-file.txt')
};
as for console.log(body)
Also file.get returns a file.resource File resource does not give you the option to see the contents of the file. Google Drive doesn't have the ability to read the file itself it can only tell you about the file.
Receiving HTTP Failure during parsing in Angular. Goal is to download a csv file from the api response
Controller:
downloadFile(data) {
const blob = new Blob([data], { type: 'text/csv' });
const url = window.URL.createObjectURL(blob);
window.open(url);
}
getFileDownload(): void {
this.iportalUploadService.getFileDownload(this.fileName).subscribe(data => {
debugger;
this.fileDownload = data;
this.downloadFile(data);
});
}
Service:
private fileDownloadUrl = 'file-transfer/validationErrorsCSV';
formHtppOptions(params): any {
const httpOptions = {
headers: { 'Application-Token': this.getToken() },
params: params,
};
return httpOptions;
}
getFileDownload(fileName): Observable < Object > {
const baseUrl = this.getBaseUrl();
return this.http.get<Object>(baseUrl + this.fileDownloadUrl, this.formHtppOptions({ fileName: fileName }));
}
Below is the console error I am receiving
console error
Response format Photo
Response photo
You are getting this error because your response is not in JSON format. You are trying to convert it into an object and CSV text cannot be parsed to a proper json object. Here is what you might want to do:
getFileDownload(fileName): Observable<any> {
const baseUrl = this.getBaseUrl();
return this.http.get(baseUrl + this.fileDownloadUrl, this.formHtppOptions({fileName: fileName})).pipe(map((data:any) => this.converter.ToJson(data)));
}
Usually, I have a "converter" service that does this kind of parsing. You can make use of papa parse, or parse yourself by looping through the response.
Update: Here is an example of manually parsing the response: http://blog.sodhanalibrary.com/2016/10/read-csv-data-using-angular-2.html
Have a look at the above blog post.
I resolved this issue by adding responseType: 'text' in formhttpOtions.
I’m using Expo’s image picker and I’m getting this output:
Object {
"cancelled": false,
"height": 468,
"uri": "file:///data/user/0/host.exp.exponent/cache/ExperienceData/%2540jbaek7023%252Fstylee/ImagePicker/9a12f0a3-9260-416c-98a6-51911c91ddf4.jpg",
"width": 468,
}
I could render my image, but I just realized that the URL is the phone’s local URI.
I’m using Redux-Thunk and Axios to send HTTP POST request:
export const sendPost = ( imageUri, title, content ) => async dispatch => {
let response = await axios.post(`${ROOT_URL}/rest-auth/registration/`, {
image, <<<<- I can't put image uri here :( it's LOCAL path
title,
content
})
if(response.data) {
dispatch({ type: POST_CREATE_SUCCESS, payload: response.data.token })
} else {
dispatch({ type: POST_CREATE_FAIL })
}
}
UPDATE I changed a request call
let headers = { 'Authorization': `JWT ${token}`};
if(hType==1) {
headers = { 'Authorization': `JWT ${token}`};
} else if (hType==2) {
headers = { 'Authorization': `Bearer ${token}`};
}
let imageData = new FormData();
imageData.append('file', { uri: image });
let response = await axios.post(`${ROOT_URL}/clothes/create/`, {
image: imageData,
text, bigType, onlyMe ...
}, {headers});
!! sorry for the complication but image variable name; image is uri for the image. I didn't want to change the name of original variable name
and on server, it's printing
'image': {'_parts': [['file', {'uri': 'file:///data/user/0/host.exp.exponent
/cache/ExperienceData/%2540jbaek7023%252Fstylee/
ImagePicker/78f7526a-1dfa-4fc9-b4d7-2362964ab10d.jpg'}]]}
I found that gzip compression is a way to send image data. Does it help?
Another option is to convert your image to base64 and send the string. Downsize is that usually the base64 strings has a bigger size than the image itself.
Something like this:
function readImage(url, callback) {
var request = new XMLHttpRequest();
request.onload = function() {
var file = new FileReader();
file.onloadend = function() {
callback(file.result);
}
file.readAsDataURL(request.response);
};
request.open('GET', url);
request.responseType = 'blob';
request.send();
}
It has to be a local URI, there's no issues with that, how else are you going to point to the image.
Now to upload the image you should first wrap it inside of FormData:
// add this just above the axios request
let img = new FormData();
img.append('file', { uri: imageUri });
Then inside of your axios request body add:
image: img,
EDIT: This question in it's current form is unanswerable.
I'm using the same Expo’s image picker with React-native in one of my projects as OP and everything works just fine, there's no issues with FormData.
After having talked with OP in a stackoverflow chat, couple of days ago, and stripping the request down to just an image, the server started throwing encoding errors:
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xff in position 168: invalid start byte
So the issue is with OP's Django backend not being setup correctly in parsing the image, and not with the sending of the image itself - which makes the question unanswerable.
you cant use react-native-fetch-blob .....
import RNFetchBlob from " react-native-fetch-blob"
PostRequest(PATH){
RNFetchBlob.fetch('POST', "[URL]", {
"x-session-id": "SESSION_ID", //or Custom headers
'Content-Type': 'multipart/form-data',
}, [
{ name: 'image', filename: 'vid.jpeg', data: RNFetchBlob.wrap(PATH) },
// custom content type
]).then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
// error handling ..
})
}
}
for reference react-native-fetch-blob