I've successfully built a React UI to select and upload N files. The key part of it is this:
<input type='file' accept='image/*' id='selectFiles' multiple onChange={handleFileChange} />
The selected files are stored in this state variable:
const [fileList, setFileList] = React.useState<FileList>();
I know they're correctly there because I iterate through them and show them in a preview DIV.
Following ImageKit's instructions, I successfully built an Auth endpoint which returns the auth credentials.
Then, within a useEffect I iterated through fileList to upload one photo at a time to the ImageKit server. But even trying just one file, I keep getting a 400 error informing me that the fileName parameter is missing. It definitely is not missing so I suspect that the problem lies with what I'm providing as the file parameter.
Here's the critical code (with some data obscured for privacy reasons) :
const uploadFile = async (file: File) => {
try {
const body = {
file: file,
publicKey: 'my_public_key',
signature: 'imageKit_signature',
expire: 'imageKit_expiry_value',
token: 'imageKit_token',
fileName: 'test123.jpg',
useUniqueFileName: false,
folder: userName,
overwriteFile: false,
};
const response = await axios.post('https://upload.imagekit.io/api/v1/files/upload', body);
console.log(response.status, response.data);
} catch (err) {
console.error(err);
}
};
Might anyone see what I'm doing wrong?
Robert
I solved the problem. If you're going to upload a file to ImageKit using their POST endpoint, you need to explicitly set the headers like this:
const response = await axios.post(
'https://upload.imagekit.io/api/v1/files/upload',
body,
{ headers: {'Content-Type': 'multipart/form-data'} }
);
For those wondering, here's the barebones body:
const body = {
file: base64File,
publicKey: imageKitPublicKey,
signature: authParams?.signature,
expire: authParams?.expire,
token: authParams?.token,
fileName: 'someFilename.ext',
};
But indeed, specifying Content-Type like above will allow you to upload a file to their server.
Related
EDIT: I've updated the CORS config but its still showing the same error.
I have a Tinymce RTE on my page, and when u drop an image into the editor, I have some functions that upload it to firebase storage, then swaps out the src of the text editor with the url fetched from firebase. It works kinda ok, but its being displayed as a broken link image icon.
When I check the link, its because originally it downloads the image when the link is clicked. I added a metadata property when it uploads it, but now its just showing a tiny box.
Here is the code where the image dropped into the editor is uploaded into firebase storage
const imagesUploadHandler = async (blobInfo, success, failure) => {
try {
const file = blobInfo.blob();
const storageRef = ref(storage, file.name);
const metadata = {
contentType: 'image/jpeg',
};
await uploadBytes(storageRef, file, metadata);
const url = await getDownloadURL(storageRef);
console.log(url);
return url;
} catch (error) {
// Call the failure callback with the error message
console.log(error.message);
}
};
Originally, i didnt include the contentType metadata, and it was just uploading as application/octet-stream, which i assume is why it prompts you to save the image.
Image link: https://firebasestorage.googleapis.com/v0/b/cloudnoise-news.appspot.com/o/ref.jpg?alt=media&token=1edc90e7-1668-4a06-92a3-965ce275798b
Currently its displaying this
Somethings i checked through
firebase storage rules is in test mode, so should be able to read and write by anyone.
i tried sticking in different MIME types but it either shows the tiny box, or it shows "undefined"
the files upload successfully and the "swap" in Tinymce editor is also all good.
Any idea why this is happening?
you need to set the metadata tag
const metadata = {
contentType: file.type,
};
This should ensure that the correct content type is set when the image is uploaded to Firebase Storage.
If this does not resolve the issue, you may need to check that the URL returned from getDownloadURL is valid and points to the correct image. You can try opening the URL in a new browser tab to verify that the image is accessible.
I fixed it by adding a blob, I created a blob object with the file data, then i just made it upload the blob object instead of the single file.
const imagesUploadHandler = async (blobInfo, success, failure) => {
try {
const file = blobInfo.blob();
const storageRef = ref(storage, file.name);
const metadata = {
contentType: file.type,
};
// Create a new Blob object with the file data
const blob2 = await new Blob([file], { type: file.type });
// Upload the Blob to Firebase Storage
await uploadBytes(storageRef, blob2, metadata);
const url = await getDownloadURL(storageRef);
console.log(url);
return url;
} catch (error) {
// Call the failure callback with the error message;;
console.log(error.message)
}
};
as the title says, I'm trying to upload a file from React front end to FastAPI. The code I used is below:
//this is backend FastAPI ==================
#app.post("/uploadfile")
async def create_upload_file(file: UploadFile = File(...)):
return {"filename": file.filename}
//frontend ===================================
const [file, uploadFile] = useState(null)
//when upload button clicked
function handleSubmit(){
console.log(file[0].name)
const formdata = new FormData();
formdata.append(
"file",
file[0],
)
axios.post("/uploadfile", {
file:formdata}, {
"Content-Type": "multipart/form-data",
})
.then(function (response) {
console.log(response); //"dear user, please check etc..."
});
}
// this is when file has been selected
function handleChange(e){
uploadFile(e.target.files); //store uploaded file in "file" variable with useState
}
It returns a 422 (Unprocessable Entity). The message detail from axios is:
I am not quite familiar with the rules and format needed behind file uploading. Could someone clear my confusion?
Update from OP:
I have managed to solve the problem by replacing the axios part with
const headers={'Content-Type': file[0].type}
await axios.post("/uploadfile",formdata,headers)
.then()//etc
if anyone want to add more information on why that works please feel free to do so - since I'm not quite sure either.
So i'm trying to send image files uploaded by my users to firebase storage using the file type input element. Like this:
<input
className={inputStyle}
{...register("donorPhotoFile")}
type="file"
accept=".png, .jpg,.jpeg"
></input>
So when I try to console.log the value returned of that input, im getting an object with the following properties:
name: "file_name.jpg",
size: ,
type: "image/png"
webkitRelativePath: ""
The /api/firebase is my api endpoint in next.js to upload my form data to firestore. From the firebase documentation, the 'file' should come from File API which I've did but its always unsuccessful and im not sure what im doing wrong.
const submitForm = async (data) => {
const imageFile = data.donorPhotoFile[0] //this is the file
const imageUpload = await fetch("/api/firestorage", {
method: "POST",
body: imageFile
});
const res = await imageUpload.json()
console.log(res)
}
//in my firestorage endpoint i've done this:
const storage = getStrorage(app) //app here is an instance of my firebase initialized
const handler = async (req, res) => {
const storageRef= ref(storage)
const imageFile = req.body
try {
uploadBytes(storageRef, imageFile);
res.status(200).json({statusRes: "success"})
} catch(error) {
res.status(400).json({statusRes: "failed", errorMessage: error})
}
}
Doing that returns a storage/invalid-root-operation error code with a message of:
"Firebase Storage: The operation 'uploadBytes' cannot be performed on a root reference, create a non-root reference using child, such as .child('file.png')
So tried to make a reference to a specific file and inserted the file name as a second parameter to storageRef like this:
const storageRef = ref(storage).child(`images/${req.body.name}`)
but its still not working but now i'm getting an empty error object so I can't figure out what's wrong now. So i actually tried checking what req.body is and it's returning this:
file object in my api endpoint
I don't understand why is it like that? And what im actually looking at? What i've sent in my post request is a File object. Like this:
File object i attached to my post request
You can create a reference to a path using Modular SDK as shown below:
const storageRef= ref(storage, `images/${req.body.name}`)
The .child() method is used in older name-spaced syntax.
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.
I've uploaded few svg image to a S3 bucket(I'v set to public-all).After I uploaded all svg images.I get each image's URL.When I clicked on those Url it just download the images for me.
Also. Does anyone know why when I use those Url in a img tag (e.g.<img src='https://***.s3.***.amazonaws.com/***.svg />`).It just shows a broken image
Here is my lambda function
'use strict'
const aws = require('aws-sdk')
const s3 = new aws.S3()
const { parse } = require('aws-multipart-parser')
const response = (statusCode, data) => ({
statusCode,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
},
body: JSON.stringify(data)
})
exports.handler = async event => {
const inputData = parse(event, true)
if (inputData.file) {
try {
const params = {
Bucket: ***,
region: ***,
Key: `${inputData.file.filename}`,
Body: inputData.file.content,
ACL: 'public-read'
}
const s3Response = await s3.upload(params).promise()
return response(200, { statusCode: 200, url: s3Response['Location'] })
} catch (error) {
console.log('Error: ', error)
return response(500, {
error: error.message
})
}
} else {
return response(400, {
error: 'Please provide input file.'
})
}
}
You need to set the content type for you svg image on S3 to "image/svg+xml".
To change the content-type through S3 console :
Select the object on the S3 console
Click on actions
Click on change metadata
Change the content-type to image/svg+xml. The value is already available in the drop down.
As you are using the API gateway to upload the images, you can set the respective content type in you putObject request.
Reference : AWS S3 Put Object API documentation
You can refer the following AWS documentation to upload image with ContentType through JS :
https://docs.aws.amazon.com/AWSJavaScriptSDK/latest/AWS/S3.html#putObject-property
I faced a similar issue with SVGs being served from S3. On opening the svg url on the browser, it was working fine, but on passing the url in the imgs src attribute gave a broken image.
After comparing my svg with other working svgs I found that below attributes were missing in my svg.
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
On adding them it started working fine.
Something like this it has to be -
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
...
...
</svg>
My Front-End developers were complaining about this same issue. Blame AWS!
For some reason, they mark SVG images as binary/octet-stream instead of marking them as image/svg+xml.
My Back-End is developed in Golang and I use the official AWS SDK to Upload the files (https://github.com/aws/aws-sdk-go). What I did to fix the problem was pass by parameter the MimeType (ContentType) of the file.
If you don't pass AWS any MimeTypes then they will try to automatically identify a MimeType for your file, however they can make a mistake in some cases. If you pass a MimeType to AWS they will use your MimeType instead of trying to identify it, and that will likely solve the problem.
In my case in Golang I used this package to be able to automatically identify the MimeType https://github.com/gabriel-vasile/mimetype
var mimeTypePtr *string
mime := mimetype.Detect(file)
if mime != nil {
mimeType := mime.String()
mimeTypePtr = &mimeType
}
uploader := s3manager.NewUploader(cfg)
result, err := uploader.Upload(&s3manager.UploadInput{
Bucket: os.Getenv("AWS_BUCKET"),
Key: filename,
Body: file,
ContentType: mimeTypePtr,
})
In my case SVG image still gets forces download if its mime-type is image/svg
You can fix it by set the mime-type for the file is image/svg+xml