Get the image from the server - reactjs

I uploaded an image file using axios post method and it was successfully uploaded to the server...
On return to the POST method, it returned the Image URL as:
{imageUrl: "/root/TTAppJava/images/players/Screenshot from 2020-11-24 16-38-57.png"}
Now, I want to display the image onto my screen.
How can I get that image?
I am using React.js to implement this.
The URL I used to post the image is:
http://139.59.16.180:8269/player/details/${id}
I am doing this to upload my data:
var formData = new FormData()
formData.append("file", image);
const theWrapper = {
"data": {
"name": name,
"age": age,
"email": email,
"gender": gender
}
}
formData.append("theWrapper", JSON.stringify(theWrapper))
Axios.post("http://139.59.16.180:8269/player/add",
formData,
{ headers: { Authorization: "Bearer " + localStorage.getItem("token") } }
).then(res => {
console.log(res)
alert("Player added successfully.")
history.push("/players")
})
.catch(err => alert(err.messge))
And the response I am getting is:
{id: 14, name: "shreyas", email: "asdfjka#gmail.com", gender: "Male", imageUrl: "/root/TTAppJava/images/players/Screenshot from 2020-11-24 16-38-57.png", …}

I will give you an example how its done in Node app, since the underlying concept will be the same as I am not a Java developer.
First please note your image_url/download_url should be saved as follows,
https://yourdomain/folder/image_name
example: http://localhost:3000/uploads_folder/my_image_name.jpg
and then you need a route in Java Spring which figures out what image to send to the front-end as Stream like below,
router.get("/:folder/:image_name", async (req, res) => {
const { folder, image_name } = req.params;
// find an image based on the downloadUrl in the folder where files are saved.
// Please note, after uploading file successfully generate a download url
// appropriately so that this route can help figure out which image you
// are looking for.
const file = path.join(`path/to/${folder}`, `${image_name}`);
// Figure out a way how stream works in your context.
// Providing MIME type is important!
const type = mime[path.extname(file).slice(1)] || "image/jpg";
const s = fs.createReadStream(file);
s.on("open", () => {
res.set("Content-Type", type);
s.pipe(res);
});
s.on("error", (err) => {
// Handle Error
});
});

Related

403 when upload file to S3 bucket using axios

I'm using axios to upload an audio file to AWS s3 bucket.
The workflow is: React => AWS API Gateway => Lambda.
Here is the backend Lambda code where generates the S3 presigned URL:
PutObjectRequest putObjectRequest = PutObjectRequest.builder()
.bucket(AUDIO_S3_BUCKET)
.key(objectKey)
.contentType("audio/mpeg")
.build();
PutObjectPresignRequest putObjectPresignRequest = PutObjectPresignRequest.builder()
.signatureDuration(Duration.ofMinutes(10))
.putObjectRequest(putObjectRequest)
.build();
PresignedPutObjectRequest presignedPutObjectRequest = s3Presigner.presignPutObject(putObjectPresignRequest);
AwsProxyResponse awsProxyResponse = new AwsProxyResponse();
awsProxyResponse.setStatusCode(HttpStatus.SC_OK);
awsProxyResponse.setBody(
GetS3PresignedUrlResponse.builder()
.s3PresignedUrl(presignedPutObjectRequest.url().toString())
.build().toString());
return awsProxyResponse;
Here is the java code to create the bucket:
private void setBucketCorsSettings(#NonNull final String bucketName) {
s3Client.putBucketCors(PutBucketCorsRequest.builder()
.bucket(bucketName)
.corsConfiguration(CORSConfiguration.builder()
.corsRules(CORSRule.builder()
.allowedHeaders("*")
.allowedMethods("GET", "PUT", "POST")
.allowedOrigins("*") // TODO: Replace with domain name
.exposeHeaders("ETag")
.maxAgeSeconds(3600)
.build())
.build())
.build());
log.info("Set bucket CORS settings successfully for bucketName={}.", bucketName);
}
In my frontend, here is the part that try to upload file:
const uploadFile = (s3PresignedUrl: string, file: File) => {
let formData = new FormData();
formData.append("file", file);
formData.append('Content-Type', file.type);
const config = {
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
//"Content-Type": file.type,
},
onUploadProgress: (progressEvent: { loaded: any; total: any; }) => {
const { loaded, total } = progressEvent;
let percent = Math.floor((loaded * 100) / total);
if (percent < 100) {
setUploadPercentage(percent);
}
},
cancelToken: new axios.CancelToken(
cancel => (cancelFileUpload.current = cancel)
)
};
axios(
{
method: 'post',
url: s3PresignedUrl,
data: formData,
headers: {
"Content-Type": 'multipart/form-data; boundary=---daba-boundary---'
}
}
)
.then(res => {
console.log(res);
setUploadPercentage(100);
setTimeout(() => {
setUploadPercentage(0);
}, 1000);
})
.catch(err => {
console.log(err);
if (axios.isCancel(err)) {
alert(err.message);
}
setUploadPercentage(0);
});
};
However, when try to upload the file, it return 403 error.
And if I use fetch instead of axios instead and it works, like this:
export async function putToS3(presignedUrl: string, fileObject: any) {
const requestOptions = {
method: "PUT",
headers: {
"Content-Type": fileObject.type,
},
body: fileObject,
};
//console.log(presignedUrl);
const response = await fetch(presignedUrl, requestOptions);
//console.log(response);
return await response;
}
putToS3(getPresignedUrlResponse['s3PresignedUrl'], values.selectdFile).then(
(putToS3Response) => {
console.log(putToS3Response);
Toast("Success!!", "File has been uploaded.", "success");
}
);
It seems to me that the only difference between these two is that: when using fetch the request's Content-Type header is Content-Type: audio/mpeg, but when using axios it is Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryClLJS3r5Xetv3rN7 .
How can I make it work with axios? I'm switching to axios for its ability to monitor request progress as I want to show an upload progress bar.
I followed this blog and not sure what I missed: https://bobbyhadz.com/blog/aws-s3-presigned-url-react
You are using POST in your axios. Should be PUT instead.
Also I think the content type has to match the one specified during requesting the pre-signed URL, which is audio/mpeg as you rightly pointed out.
Correspondingly, your data should be just file, instead of formData.
axios(
{
method: 'put',
url: s3PresignedUrl,
data: file,
headers: {
"Content-Type": 'audio/mpeg'
}
}
...
You didn't mark any answers as accepted so I guess you didn't solve it.
For any future viewers out there. The reason why you are getting 403 forbidden error is because your Content-Type in your server and client side are not matching. I'm assuming you set up the AWS policies correctly.
Your code in the backend should look like this:
const presignedPUTURL = s3.getSignedUrl("putObject", {
Bucket: "bucket-name",
Key: String(Date.now()),
Expires: 100,
ContentType: "image/png", // important
});
and in the front-end (assuming you are using axios):
const file = e.target.files[0]
const result = await axios.put(url, file, {
withCredentials: true,
headers: { "Content-Type": "image/png" },
});
In practical, you would normally have to send the file type to generate the pre-signed url in the POST body or whatever and then in axios you do file.type to get the file type of the uploaded file.
Check your Lambda execution role. It may be the culprit. Perhaps it does not grant enough permissions to allow PUTting files into your bucket.
URL signing is a delegation of power on behalf of the signer, which is restricted to a specified object, action... Signing does not magically grants full read/write permissions on S3, even on the specific object related to the presigned URL.
The "user" who generates the signature requires sufficient permissions to allow the actions you want to delegate through that presigned URL. In this case, this is the execution role of your Lambda function.
You can add the AmazonS3FullAccess managed policy to the execution role and see if it solves your situation. This change took me out of a blocked situation me after days of struggle. Afterwards, before going to production, restrict that rule to the specific bucket you want to allow uploads into (least privilege principle).
If you develop using SAM local emulation, those execution roles seem not to be taken into account as long as you run your functions locally; the signed links work in that context even without S3 permissions.

POST method JSON not updating JSON server local file

I have setup a POST method on the submit handler of my react app. But every time I submit the form, the JSON server just registers the id, and not the content, as shown below:
Output Image
Json Server local file
As you can see, the app can display the user input's newly submitted form, but it can't be registered in the json server, as it only shows the id. Here is the snippet for the Submit handler function of the form:
const formSubmitHandler = (event) => {
event.preventDefault();
if (!enteredFNameValid || !enteredLNameValid) {
alert("First Name and Last Name accepts letters only.");
return;
}
const newInputData = {
fName: enteredFName,
lName: enteredLName,
email: enteredEmail,
eid: enteredEid,
birthday: enteredBirthday,
};
const allInputData = [...inputData, newInputData];
setInputData(allInputData);
// console.log(allInputData);
fetch("http://localhost:3001/inputData/", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify[newInputData],
}).then((res) => {
console.log("new post added");
alert("Success!");
});
};
I've tried to change the body to an object as {newInputData}, but it just creates a new set of array, and registers that as the new entry for the JSON's generated id.
Hope to have insights from ya'll. Thank you.
Maybe it's an issue with square brackets on the JSON.stringify?
Did you take a look at this post?

How to post data when you have javascript object using multipart/form-data content-type

So I have never post a data using FormData and multipart/form-data as Content-Type in my React project. But now I'm kinda forced by backend to send it this way since we have some images in payload.
The problem is that the whole data is a JS object and can be parsed to JSON, and nothing more. So how can I convert my JS object into a valid FormData that backend would accept it? Everything works without a problem in Postman, but in the app I always get the same error.
Here is some more detail:
The working Postman sample:
What I expect to be working (but obviously doesn't):
const createProd = () =>
HttpRequest.post('/api/prod', {
body: {
Product: {
title: 'Test Prod',
shop: null,
description: "My new ad's description",
category: { id: '5d8c6aa6fadeaf26b0194667' },
condition: 'USED'
}
});
HttpRequest is a helper function which uses ES6 fetch for requests.
I always get the same error: "Required request part 'Product' is not present" with/without JSON.stringify.
I even tried to create a sample FormData to at least change the error:
cont payload = new FormData();
payload.append('Product', {foo: 'bar'});
But still same error. I also copied the code which is generated by Postman. But still no chance.
I would be thankful if you share your suggestions or workarounds.
Thanks in advance.
const formData = new FormData();
const product = { //your product object };
formData.append('product', JSON.stringify(product));
const config = {
headers: {
'Content-Type': 'multipart/form-data; charset=utf-8; boundary="another cool boundary";'
}
};
axios.post(`/api/ads`, formData, config).then((res) => {
console.log(res);
}).catch(err => {
console.log(err);
});
Maybe you should set header.
Try this one. In my case I used Axios. It worked for me in my project.

React JS + PDFKit

I am trying to understand how one might get PDF Kit working within React JS.
My simple requirement is to render a file in the browser using ReactJS and PDFKit.
Looking at the tutorials, there are references to a few options in which PDFKit can work in the browser, however it is unclear how this would apply in the world of React JS and in particular a Create React App based project...
http://pdfkit.org/demo/browser.html
https://www.npmjs.com/package/pdfkit#browser-usage
Has anyone come across a working example with Create React App based React JS and PDFKit?
Google seems to be a bit short on answers this evening.
I wanted to do exactly this (PdfKit client-side rendering in a React app bootstrapped with create-react-app). I managed to get it working using pdfkit-webpack-example and customize-cra.
Source
Live Demo
I manage to find an answer for this.
It's a combination of this and this.
But just to give an overview/key codes. In my react application, I have this (make a post request to my API server):
<Button
onClick={(e) => {
const { _id } = record;
fetch(`/api/pdf`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
filename: "test",
content: "Testing Content"
}),
}).then(async res => {
if (res.status === 200) {
const blob = await res.blob();
const file = new Blob(
[blob],
{type: 'application/pdf'}
);
//Build a URL from the file
const fileURL = URL.createObjectURL(file);
//Open the URL on new Window
window.open(fileURL);
}
})
>
Download
</Button>
And in the API server (node express application), I have this:
const postMethod = () => async (req, res, next) => {
const doc = new PDFDocument()
let filename = req.body.filename
// Stripping special characters
filename = encodeURIComponent(filename) + '.pdf'
// Setting response to 'attachment' (download).
// If you use 'inline' here it will automatically open the PDF
res.setHeader('Content-disposition', 'attachment; filename="' + filename + '"')
res.setHeader('Content-type', 'application/pdf')
const content = req.body.content
doc.y = 300
doc.text(content, 50, 50)
doc.pipe(res)
doc.end()
}

Multiple file uploads to Cloudinary with Axios in React

I have tried implementing the superagent way of uploading multiple files in axios. But somehow, I'm getting an error in console
Failed to load https://api.cloudinary.com/v1_1/xxxx/image/upload:
Request header field Authorization is not allowed by
Access-Control-Allow-Headers in preflight response.
My upload handler looks like this
uploadFile(){
const uploaders = this.state.filesToBeSent.map(file => {
const formData = new FormData();
formData.append("file", file);
formData.append("upload_preset", "xxxxx");
formData.append("api_key", "xxxxx");
formData.append("timestamp", (Date.now() / 1000) | 0);
return axios.post(url, formData, {
headers: { "X-Requested-With": "XMLHttpRequest" },
}).then(response => {
const data = response.data;
const fileURL = data.secure_url
console.log(data);
})
});
// Once all the files are uploaded
axios.all(uploaders).then(() => {
// ... perform after upload is successful operation
console.log("upload completed ", uploaders);
});
}
I have got this example from here
Another thing is confusing to me. In superagent we can attach parameters to the request field which includes API Secret Key of Cloudinary like this:
const paramsStr = 'timestamp='+timestamp+'&upload_preset='+uploadPreset+secretKey;
const signature = sha1(paramsStr);
const params = {
'api_key': 'xxxx',
'timestamp': timestamp,
'upload_preset': uploadPreset,
'signature': signature
}
Object.keys(params).forEach(key => {
uploadRequest.field(key, params[key])
});
But in that example, it is not mentioned how to append the secret key and other params to axios.
You will need to generate the signature on your backend, and then perform the upload with the generated signature.
You can generate a signature via the following instructions- https://support.cloudinary.com/hc/en-us/articles/203817991-How-to-generate-a-Cloudinary-signature-on-my-own-
You can also take a look at the following example on how to append the signature to your request. It's in PHP, however, the guidelines still apply.
https://gist.github.com/taragano/a000965b1514befbaa03a24e32efdfe5

Resources