Send FormData object and File in a single Axios POST request - reactjs

I currently have a Spring boot API controller method (below) that accepts an object as well as a MultiPart file. I am able to successfully send a POST request via Postman however I am now struggling to make this post request via my front-end ReactJS application using Axios.
#PostMapping(
path = "/upload",
consumes = {
MediaType.APPLICATION_JSON_VALUE,
MediaType.MULTIPART_FORM_DATA_VALUE
},
headers = {
"Content-Type=multipart/form-data"
}
)
public SoundProfile uploadSoundProfile(Authentication auth,
#RequestPart("soundProfileRequest") SoundProfileRequest soundProfileRequest,
#RequestPart("audio_file") MultipartFile audio_file){
return soundProfileService.uploadSoundProfile(auth, soundProfileRequest, audio_file);
}
Postman POST request:
The following is my service method to process the object and file which is responsible for saving the object to a MySQL database and then storing the file in an Amazon S3 bucket.
public SoundProfile uploadSoundProfile(Authentication auth, SoundProfileRequest soundProfileRequest, MultipartFile audio_file) {
if (audio_file.isEmpty()){
throw new IllegalStateException("no audio file received");
}
AppUser current_user = appUserRepository.findByEmail(auth.getName())
.orElseThrow(
() -> new IllegalStateException("User not found")
);
Map<String, String> metadata = new HashMap<>();
metadata.put("Content-Type", audio_file.getContentType());
metadata.put("Content-Length", String.valueOf(audio_file.getSize()));
String soundPath = UUID.randomUUID().toString();
SoundProfile soundProfile = new SoundProfile(
soundPath, // SoundPath = S3 key
soundProfileRequest.getCaseBrand(),
soundProfileRequest.getCaseModel(),
soundProfileRequest.getSwitches(),
soundProfileRequest.getKeycaps(),
soundProfileRequest.getStabilizers(),
soundProfileRequest.getLube(),
soundProfileRequest.getMods(),
current_user
);
// save sound profile to database
soundProfileRepository.save(soundProfile);
String path = String.format("%s/%s", BucketName.KEYBOARD_AUDIO_BUCKET.getBucketName(), current_user.getUserId());
String filename = String.format("%s-%s", audio_file.getOriginalFilename(), soundPath);
// Save audio file to s3 bucket
try {
fileStore.saveAudio(
path,
filename,
Optional.of(metadata),
audio_file.getInputStream()
);
} catch (IOException e) {
throw new IllegalStateException(e);
}
return soundProfile;
}
I would like to send the SoundProfileRequest object and the multipart file separately, meaning I don't want to append the file to a FormData object, but I would still like to send the file along with the form fields in a single post request.
For example in my front-end React Component:
export default function UploadSoundProfile() {
const [caseBrand, setCaseBrand] = useState("");
const [caseModel, setCaseModel] = useState("");
const [switches, setSwitches] = useState("");
const [keycaps, setKeycaps] = useState("");
const [lube, setLube] = useState("");
const [stabilizers, setStabilizers] = useState("");
const [mods, setMods] = useState("");
const [selectedFile, setSelectedFile] = useState("");
const history = useHistory();
const createSoundProfile = (e) => {
e.preventDefault();
const url = "/sound-profile/upload";
const formData = new FormData();
formData.append('caseBrand', caseBrand);
formData.append('caseModel', caseModel);
formData.append('switches', switches);
formData.append('keycaps', keycaps);
formData.append('lube', lube);
formData.append('stabilizers', stabilizers);
formData.append('mods', mods);
**// SHOULD FILE ALSO BE APPENDED TO FORMDATA OBJECT HERE?**
formData.append('audio_file', selectedFile);
const config = {
headers: {
"content-type": "multipart/form-data"
}
}
uploadProfileService.createSoundProfile(url, formData, config);
history.push("/sound-profile/profile");
};
return (
...
)
}
Is there a way to make the POST request with Axios without appending the file to the FormData object while still making a single post request?
I am unsure of how to accomplish this, or if it is possible. I have seen other posts where a file is being .append() to a FormData object, but I am unsure if this will cause an error on the backend.
Thanks for any help in advance!

Related

`S3Client.send()` doesn't return `VersionId`

I have a bucket which has versioning enabled. In my ReactJS app, I need to upload files to the bucket and receive the new object metadata. I use S3 Client with PutObjectCommand to do the upload. The documentation here states:
Versioning
If you enable versioning for a bucket, Amazon S3 automatically generates a unique version ID for the object being stored. Amazon S3 returns this ID in the response. When you enable versioning for a bucket, if Amazon S3 receives multiple write requests for the same object simultaneously, it stores all of the objects.
So, I expect to receive a VersionId. But not only this field is undefined, but also other fields like requestId or `cfId.
Here is my module code:
import {
PutObjectCommand,
S3Client
} from '#aws-sdk/client-s3';
const S3_BUCKET_NAME = process.env.S3_BUCKET_NAME;
const AWS_REGION = process.env.AWS_REGION;
const AWS_ACCESS_KEY = process.env.AWS_ACCESS_KEY;
const AWS_SECRET_KEY = process.env.AWS_SECRET_KEY;
const client = new S3Client({
region: AWS_REGION,
credentials: {
accessKeyId: AWS_ACCESS_KEY,
secretAccessKey: AWS_SECRET_KEY
}
});
const uploadToS3 = async function (fileToUpload) {
const data = await fileToUpload.arrayBuffer();
const params = {
Bucket: S3_BUCKET_NAME,
Key: fileToUpload.name,
Body: data
};
const command = new PutObjectCommand(params);
const result = await client.send(command);
console.log(result); // requestId: undefined, extendedRequestId: undefined, cfId: undefined
console.log(`VersionId: ${result.VersionId}`); // VersionId: undefined
}
export default {
uploadToS3
}
Have I missed anything here?

Uploading image to azure blob storage using React

I am trying to upload image from react to azure blob storage but the request fails with error :
TypeError: Cannot read properties of undefined (reading 'size')
at BlockBlobClient.uploadFile
here is a sample of code trying to achieve it :
import { BlobServiceClient } from '#azure/storage-blob';
const account = process.env.REACT_APP_AZURE_ACCOUNT_NAME;
const sas = process.env.REACT_APP_SAS_TOKEN;
const containerName = 'usercontainer';
const blobServiceClient = new BlobServiceClient(
`https://${account}.blob.core.windows.net/?${sas}`,
);
export const uploadToBlob = async (file) => {
const containerClient = blobServiceClient.getContainerClient(containerName);
const blobName = file.src + new Date().getTime();
const blockBlobClient = containerClient.getBlockBlobClient(blobName);
const uploadBlobResponse = await blockBlobClient.uploadFile(file.src);
console.log(
`Upload block blob ${blobName} successfully`,
uploadBlobResponse.requestId,
);
};
There are a few issues here:
You are calling uploadFile method which is only available in NodeJS runtime and is not available in the browser. Please see documentation of this method here: https://learn.microsoft.com/en-us/javascript/api/#azure/storage-blob/blockblobclient?view=azure-node-latest##azure-storage-blob-blockblobclient-uploadfile.
The method you would want to use is uploadData which expects an object of type Blob. Considering you have a data URL, you would need to create a Blob object out of it. Please see this question regarding converting a data URL to Blob: Blob from DataURL?

How to keep the original filename in React when fetched from Django?

I'm working with a Django-Graphene-React stack and in my frontend, the user must be able to download a file from the backend.
It works perfectly.
However, the filename is defined in the backend and sent through the Content-Disposition header in the HttpRequest sent from Django. But I cannot retrieve it when I fetch it in the frontend.
Here is my backend Django view that generates the file:
import io
from django.http import HttpResponse
def download_file_view(request):
buffer = io.BytesIO()
# Generate an Excel file and put it a buffer...
...
buffer.seek(0)
response = HttpResponse(
buffer.read(),
content_type="application/vnd.openxmlformats-officedocument"
".spreadsheetml.sheet",
)
# The filename is generated in the backend (Django) and cannot be
# generated from the frontend (React)
response["Content-Disposition"] = "filename.xlsx"
return response
If I download it directly by entering the url assign to this view in my browser, I get the filename correctly when my browser prompt me to save the file.
However, I want to fetch the file in the frontend in order to get a loading state during the time the file is being generated in the backend.
Here is my frontend component in React:
import {useState) from "react";
const DownloadButton = ({downloadUrl}) => {
const [isLoading, setIsLoading] = useState(false);
const fetchExport = async() => {
try {
setIsLoading(true);
const response = await fetch(downloadUrl);
const header = response.headers.get("Content-Disposition"); // Empty
console.log(response); // Headers are empty
const blob = await response.blob();
let objectUrl = window.URL.createObjectURL(blob);
let link = document.createElement("a");
link.href = objectUrl;
// By the way, these two lines clears my console and sometimes my
// development server stops, I don't know why yet.
link.click();
link.remove();
} catch (error) {
console.log(error);
} finally {
setIsLoading(false);
}
}
if (isLoading){
return <div>Loading...</div>
} else {
return <div onClick={fetchExport}>Download</div>
}
So I get the file. This works fine. But instead of the original filename, I get my frontend url and some random uuid as filename.
I needed to add this in the django settings (using corsheaders).
CORS_EXPOSE_HEADERS = ['Content-Disposition']

Error 400 Bad Request while Uploading Image to firebase storage in React Native

I am working on react native project connected to firebase. I am =using firebase storage ad=nd trying to upload a file to firebase storage But I get following error.
{code: 400, message: "Bad Request. Could not access bucket quickbuy-a0764.appspot.com","status":"Access_Bucket"}
I tried configuring my permissions but did not work for me.
example of Image Uri I am providing to put() is as follows
data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAk and so on
Now what should I do to resolve this issue?
let filename = values.images + Date.now();
let uri = values.images[0];
const uploadTask = storage.ref(`images/${filename}`).put(uri);
uploadTask.on("state_changed", (snapshot) => {
console.log(snapshot);
});
firebase.storage.Reference#put() accepts a Blob, Uint8Array or an ArrayBuffer. Because you are trying to upload a Data URI, which is a string, you need to use [firebase.storage.Reference#putString()`](https://firebase.google.com/docs/reference/js/firebase.storage.Reference#putstring).
To do this for a data URI, you would use:
someStorageRef.putString(uri, firebase.storage.StringFormat.DATA_URL);
Next, based on these lines:
const filename = values.images + Date.now();
let uri = values.images[0];
values.images is an array, which means that filename will end up being something similar to "[object Object],[object Object]1620528961143".
As I covered in this answer on your question yesterday, this is a poor way to generate IDs as it can lead to duplicates & collisions - use a Push ID instead.
const uri = /* ... */;
const rootRef = firebase.database().ref();
const filename = rootRef.push().key;
const uploadTask = storage.ref(`images/${filename}`)
.putString(uri, firebase.storage.StringFormat.DATA_URL);
uploadTask.on("state_changed", (snapshot) => {
console.log(snapshot);
});
Future Use with Version 9 of the SDK
import { getStorage, ref, uploadBytes } from "firebase/storage";
const uploadImage = async (values) => {
const filename = values.images + Date.now();
const uri = values.images[0];
// Create a root reference
const storage = getStorage();
// Create a reference to 'images/$filename.jpg'
const filesImagesRef = ref(storage, 1images/${filename}.jpg);
await uploadBytes(filesImagesRef, uri).then((snapshot) => {
console.log('Uploaded a blob or file!');
});
}
Let us know how this works for you!

How to upload files using PrimeReact FileUpload Component

I am a React/Spring beginner and I dont know how to make a Fileupload with Primereact.
Their Documentation says following (https://www.primefaces.org/primereact/#/fileupload):
FileUpload requires a url property as the upload target and a name to identify the files at backend.
<FileUpload name="demo" url="./upload"></FileUpload>
But it neither say how do I fetch the data, how I access the "demo" in the backend, nor what kind of reponse I get.
Can anyone help me please? I have already searched the web but did not find anything.
You can do something like this:
<FileUpload name="invoice"
accept="image/*"
customUpload={true}
uploadHandler={invoiceUploadHandler}
mode="basic"
auto={true}
chooseLabel="Upload invoice"/>
const invoiceUploadHandler = ({files}) => {
const [file] = files;
const fileReader = new FileReader();
fileReader.onload = (e) => {
uploadInvoice(e.target.result);
};
fileReader.readAsDataURL(file);
};
Send your request like this
const uploadInvoice = async (invoiceFile) => {
let formData = new FormData();
formData.append('invoiceFile', invoiceFile);
const response = await fetch(`orders/${orderId}/uploadInvoiceFile`,
{
method: 'POST',
body: formData
},
);
};
Important: Do not set any Content-Type header! This will be done automatically.

Resources