Trying to upload files to firebase storage but its not working. Error object is empty, how do I figure out what's wrong? - reactjs

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.

Related

Firebase Storage not displaying image properly (shows a small box)

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

Format to POST a file to the ImageKit server?

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.

How to upload a file from React front end to FastAPI?

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.

'Eperm: operation not permitted' when using multer

I'm attempting to pass a video file from my front end (using React and Axios) and upload it to youtube from my backend using express (using this tutorial https://youtu.be/xhiWEpU-h-A). The file gets submitted in the form of a 'formdata' object from my front end.
Here's my backend
const oAuth = youtube.authenticate({type: 'oauth',client_id: credentials.web.client_id,client_secret: credentials.web.client_secret,//Redirect uris has not been set up, may cause errors
redirect_url: credentials.web.redirect_uris[0]
})
const storage = multer.diskStorage({destination: '/',filename(req, file, cb) {const newFileName = ${uuid()}-${file.originalname}cb(null,newFileName);}})
const uploadVideoFile = multer({storage: storage}).single("videoFile");
app.post('/api/uploadVideo', uploadVideoFile, (req, res)=>{
console.log("upload video endpoint established")console.log(file was set to: ${req.file})if(req.file)console.log('we found a file')
else
console.log('no file?')console.log(title: ${title} description: ${description})return;
//ignore the stuff below here
Here's the function that submits the formdata on the front end, as well as the state variables
const uploadVideo = ()=>{
//uploadVideo(Credentials, [{}])
const videoData = new FormData()
videoData.append("videoFile", videoFile)
videoData.append("title", videoTitle)
videoData.append("description", videoDescription)
console.log(videoData)
Axios.post("http://localhost:3001/api/uploadVideo", videoData).then((response) =>{
console.log(response.data)
})
}
const [videoTitle, setVideoTitle] = useState('')
const [videoMode, setVideoMode] = useState(true)
const [videoFile, setVideoFile] = useState(null)`
And here's the form object that accepts the video file
<Form.Control type="file" accept="video/mp4" disabled={!videoMode} onChange={(e)=>{setVideoFile(e.target.files[0])}}>
</Form.Control>`
When I attempt to run the function I get the following error message on my server
Error: EPERM: operation not permitted, open 'C:\fb434fe2-e46c-4d8c-8f41-7b807f1b92a7-Column test - Google Chrome 2022-01-12 17-27-21.mp4'
the error occurs before the post request can be resolved
I was hoping multer would be able to locate the file I passed to it but I simply get a permission error.
I managed to solve the error by setting up Multer again and using the default settings from the documentation
const upload = multer({dest: "uploads/"});//uploads to "uploads" folder on my server
//post request
app.post('/api/uploadVideo', upload.single("videoFile"), (req, res)=>{
const {title, description} = req.body;
console.log("upload video endpoint established")
console.log(`file destination was set to: ${req.file.destination}`)
//return

upload image to S3 presigned url using react-native-image-picker and axios

I am trying to get an presigned url image upload working correctly. Currently the upload succeeds when selecting an image from the IOS simulator, however when I actually try to view the file it seems the file is corrupted and will not open as an image. I suspect it has something to do with my FormData but not sure.
export async function receiptUpload(file) {
const date = new Date();
const headers = await getAWSHeaders();
const presignUrl = await request.post(
urls.fileUpload.presignUpload,
{file_name: `${date.getTime()}.jpg`},
{headers}
)
.then(res => res.data);
const formData = new FormData();
formData.append('file', {
name: `${date.getTime()}.jpg`,
uri: file.uri,
type: file.type
});
const fileUpload = presignUrl.presignUrl && await request.put(
presignUrl.presignUrl,
formData
)
.then(res => res.status === 200);
}
I have tried from other fixes to change the file uri like so...
Platform.OS === 'android' ? file.uri : file.uri.replace('file://', '');
however this does not seem to work either.
I did this just recently in my current project and the following code is a working example for my use case. I didn't need to convert to a blob either though I am uploading to AWS S3 so if you are uploading elsewhere that may be the issue.
export const uploadMedia = async (fileData, s3Data, setUploadProgress = () => {}) => {
let sendData = { ...fileData };
sendData.data.type = sendData.type;
let formData = new FormData();
formData.append('key', s3Data.s3Key);
formData.append('Content-Type', fileData.type);
formData.append('AWSAccessKeyId', s3Data.awsAccessKey);
formData.append('acl', 'public-read');
formData.append('policy', s3Data.s3Policy);
formData.append('signature', s3Data.s3Signature);
formData.append('file', sendData.data);
return axios({
method: 'POST',
url: `https://${s3Data.s3Bucket}.s3.amazonaws.com/`,
data: formData,
onUploadProgress: progressEvent => {
let percentCompleted = Math.floor((progressEvent.loaded * 100) / progressEvent.total)
setUploadProgress(percentCompleted);
}
})
}
I would first check to see where the issue is occurring. After uploading can you view it on whatever storage service you are trying to upload it to. If so it's something on React Native side. If it doesn't ever get uploaded to the location you know its an error in your upload process. Might help you track the exact location of the error.
I had to do this recently for a project. I believe the data is a base64 string when coming directly from the file input. So the issue is your are uploading a base64 string not the image by simply passing the data field. I had to process it before uploading to the signed URL with the following method.
private dataUriToBlob(dataUri) {
const binary = atob(dataUri.split(',')[1]);
const array = [];
for (let i = 0; i < binary.length; i++) {
array.push(binary.charCodeAt(i));
}
return new Blob([new Uint8Array(array)], { type: 'image/jpeg' });
}
This answer fixed it for me: How can I upload image directly on Amazon S3 in React Native?
I had tried uploading with axios and fetch with FormData. The download went through but the image file was not readable, even when downloaded to my Mac from the S3 console:
The file "yourfile.jpg" could not be opened. It may be damaged or use a file format that Preview doesn’t recognize.
Only after trying to upload with XHR with the correct Content-Type header did it work. Your signedUrl should be correct as well, which seems to be the case if the download goes through.

Resources