I am quite new new Flask and Blobs in general, but I've been trying for some time to send a .wav file from my frontend to my backend. In general it seems like I should put the file into a FormData(), and send a post-request to the backend.
Here is my frontend code:
import React from "react";
import { DropzoneArea } from "material-ui-dropzone";
import axios from "axios";
const DropzoneAreaExample = () => {
const headers = {
"content-type": "multipart/form-data",
};
const fileDrop = (files: File[]) => {
const formData = new FormData();
const file: File = files[0];
formData.append("file", file);
axios
.post("http://localhost:5000/analyze", { formData }, { headers })
.then((res) => console.log(res.data));
};
return (
<div>
<DropzoneArea onDrop={fileDrop} />
</div>
);
};
export default DropzoneAreaExample;
And on the backend I am trying this:
import flask
from flask import request
from flask_cors import CORS
app = flask.Flask(__name__)
CORS(app)
#app.route('/analyze', methods=['GET', 'POST'])
def analyze_data():
if request.method == 'POST':
f = request.files['file']
f.save()
return "test"
Any help or nudge in the right direction would be much appreciated!
When you pass the data to Axios post as the second parameter, you do not put it in an object.
axios
.post("http://localhost:5000/analyze", formData, { headers })
.then((res) => console.log(res.data));
I finally found a solution. The problem was in the axios-request. I transformed it to a fetch-request, and then everything worked out. Here's the working request from the frontend:
const FileUploader = () => {
const fileDrop = (files: File[]) => {
const formData = new FormData();
const file: File = files[0];
formData.append("file", file);
fetch("http://localhost:5000/upload", {
method: "POST",
body: formData,
}).then((res) => {
console.log(res.text);
});
};
Related
I am using axios to hit an API to upload a .apk file onto a 3rd party app which is running locally on my computer. Using the API on Postman is giving the desired result but while integrating it with my React app I am getting POST http://localhost:8000/api/v1/upload 400 (Bad Request) error.
I have the following with structure:
src/httpRequest.js
import axios from "axios";
export default axios.create({
baseURL: "http://localhost:8000",
headers: {
"Content-type": "application/json",
Authorization: <API_KEY>
}
});
src/services/Upload.js
import http from "../httpRequest";
const upload = (file, onUploadProgress) => {
const formData = new FormData();
formData.append("file", file);
return http.post("/upload", formData, {
headers: {
"Content-Type": "multipart/form-data",
Authorization:
<API_KEY>
},
onUploadProgress,
});
};
export default {
upload,
};
src/components/ApkUpload.js
import React, { useState, useEffect } from 'react';
import axios from 'axios';
const ApkUpload = () => {
const [selectedFiles, setSelectedFiles] = useState(undefined);
// eslint-disable-next-line no-unused-vars
const [currentFile, setCurrentFile] = useState(undefined);
const [progress, setProgress] = useState(0);
const [message, setMessage] = useState('');
const [fileDetails, setFileDetails] = useState([]);
const handleUpload = async () => {
const data = new FormData();
data.append('file', selectedFiles);
try {
const res = await axios.post('http://localhost:8000/api/v1/upload', data, {
headers: {
'Content-Type': 'multipart/form-data',
Authorization: <API_KEY>,
},
onUploadProgress: (progressEvent) => {
setProgress(parseInt(Math.round((progressEvent.loaded * 100) / progressEvent.total), 10));
},
});
} catch (err) {
if (err.response.status === 500) {
setMessage('There was a problem with the server');
} else {
setMessage(err.response.data.message);
}
}
};
const handleChange = (e) => {
setSelectedFiles(e.target.files);
setCurrentFile(e.target.files[0]);
};
useEffect(() => {
axios.get("http://localhost:8000/api/v1/scans", {
headers: {
Authorization:
<API_KEY>,
},
}).then(res => {
setFileDetails(res.data.content);
});
},[]);
return (
<div className="container">
// simple button calling above methods
</div>
);
};
export default ApkUpload;
I am using MobSF as my third party app and for upload they require multipart/form-data.
While using postman I was able to get the desired result but I'm not able to do so with my frontend. Any help regarding this issue will be highly appreciated!
const data = new FormData();
data.append('file', selectedFiles[0]);
Inside your handleUpload function selectedFiles state is of type FileList but it should be File.
If you are handling single file then you can use:
data.append('file', selectedFiles[0]);
For multiple files you can do:
for(let i=0;i<selectedFiles.length;++i){
data.append('file',selectedFiles[i])
}
Bit strange to get " access to fetch URL blocked by cors preflight etc..." because the PUT request works for me in postman but i only get that error when i try to upload in react js . Here is my code above.
I get another error by the way "
error TypeError: Failed to fetch
at Upload (Upload.js:28:1)
Which i have no clue why i am getting a type error . Any help would be appreciated
import axios from 'axios';
import React,{Component} from 'react';
import { useState } from 'react';
export default function Upload(){
const [data,setData] = useState()
if (data) {
const file = (data.target.files[0])
console.log(file)
const myHeaders = new Headers();
myHeaders.append("Content-Type", "image/jpeg");
const formdata = new FormData();
formdata.append('File', file);
const requestOptions = {
method: 'PUT',
headers: myHeaders,
body: formdata,
redirectXXX: 'manual'
};
fetch("https://XXXX.execute-api.us-east-1.amazonaws.com/prod/XXXX/IMG_0733.jpg", requestOptions)
.then(response => response.text())
.then(result => console.log(result))
.catch(error => console.log('error', error));
}
return (
<div>
<form>
<input type="file" onChange={(e) => setData(e)}/>
</form>
</div>
)
}
expecting upload to put into API same way the postman does it.,
When I try to use Axios in serverSideProps I get a strange data value, at first, I thought it was a Redux issue, but no, if I replace Axios with Fetch everything works correctly. Outside of serverSideProps Axios also works well.
export async function getServerSideProps() {
const res = await axios.get(
`https://jsonplaceholder.typicode.com/posts/`
);
console.log(res.data);
// const res = await fetch(`https://jsonplaceholder.typicode.com/posts/`);
// console.log(await res.json());
return {
props: {}
};
}
value I get
codeSandbox example
The problem is with axios v1.2.0 itself. You need to add Accept and Accept-Encoding headers as a temporary solution until the issue is solved:
const res = await axios.get('https://jsonplaceholder.typicode.com/posts', {
headers: {
Accept: 'application/json',
'Accept-Encoding': 'identity'
}
})
Github discussion about this problem.
It's a bug in Axios - I think dealing with gzip encoding. You can see the issue here.
Workaround:
import { createAsyncThunk } from "#reduxjs/toolkit";
import axios from "axios";
export const fetchJob = createAsyncThunk(
"users/fetchJobs",
async () => {
const res = await axios.get("https://jsonplaceholder.typicode.com/posts", {
headers: { Accept: "application/json", "Accept-Encoding": "identity" }
});
const data = res.data;
// const res = await fetch(`https://jsonplaceholder.typicode.com/posts`);
// const data = await res.json();
console.log(res);
return data;
}
);
Up to this point a file can be viewed on input:
export async function store(input) {
console.log("input", input);
return httpClient.post(`${apiEndpoint}`, input);
}
On above console.log, it shows data as:
But, on the serverside laravel, if I print_r($request->all()) it shows data as:
My http client looks like this:
import axios from "axios";
const apiURL = process.env.MIX_SPA_URL;
axios.defaults.headers.common["Content-Type"] = "application/json";
axios.defaults.headers.common["X-Requested-With"] = "XMLHttpRequest";
axios.defaults.withCredentials = true;
let client = axios.create({
baseURL: apiURL,
});
axios.interceptors.response.use(null, (error) => {
const expectedError =
error.response &&
error.response.status >= 400 &&
error.response.status < 500;
if (!expectedError) {
console.log("error from httpClient >>", error);
}
return Promise.reject(error);
});
function setJwt(token) {
client.defaults.headers.common["Authorization"] = "Bearer " + token;
}
const httpClient = {
get: client.get,
post: client.post,
put: client.put,
delete: client.delete,
setJwt,
};
export default httpClient;
Also, in case if you want to look how I have created input file using react-hook-form as:
<input
className={`form-control w-full ${
errors["cover_image"] ? "border-red-500" : ""
}`}
type="file"
{...register("cover_image")}
/>
Why are the images not being sent to the server?
In case of laravel, I am using laravel sanctum in combination with fortify. And, the middleware added for this route are auth:sanctum and verified.
Also, I have tried by adding headers as: "Content-Type": "multipart/form-data",
export async function store(input) {
console.log("input", input);
return httpClient.post(`${apiEndpoint}`, input, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
But, with this header, not a single data got send to the server. Here, is the screenshot:
I think you must put your file in formData and then pass it as your post request data
export async function store(input) {
const formData = new FormData();
formData.append("cover_image", input.cover_image[0]);
formData.append("blockchain", input.blockchain);
formData.append("description", input.description);
formData.append("name", input.name);
return await httpClient.post(`${apiEndpoint}`, formData, {
headers: {
"Content-Type": "multipart/form-data",
},
});
}
I am trying to upload image to aws s3 using react and drf. I am following this heroku documentation . I am getting error mentioning Request failed with status code 400.
as they have suggested in the link first I created signed url from backend.
urlpatterns = [
path('api/create-permission/aws-s3/', SignS3Upload.as_view(), name='aws-s3'),
]
import os
import boto3
import mimetypes
s3 = boto3.client('s3')
class SignS3Upload(APIView):
# authentication_classes = (authentication.SessionAuthentication,)
# permission_classes = [IsAuthenticated, ]
def get(self, request):
s3_bucket = os.environ.get('AWS_STORAGE_BUCKET_NAME')
file_name = request.GET['image_name']
file_type = mimetypes.guess_type(file_name)[0]
presigned_post = s3.generate_presigned_post(
Bucket=s3_bucket,
Key=file_name,
Fields={"acl": "public-read", "Content-Type": file_type},
Conditions=[
{"acl": "public-read"},
{"Content-Type": file_type}
],
ExpiresIn=3600
)
data = {
"signed_url": presigned_post,
'url': 'https://%s.s3.amazonaws.com/%s' % (s3_bucket, file_name)
}
return Response(data)
In frontend I am using React and Redux. here how I am sending request from frontend
export const getSignedRequest = (image) => (dispatch, getState) => {
const image_name = image.name
axios.get('https://my-site.herokuapp.com/api/blog/api/create-permission/aws-s3/', { params: { image_name } })
.then((res) => {
dispatch({
type: GET_PARTICULAR_BLOG_IMG_UPLOAD,
payload: res.data
});
var postData = new FormData();
for (key in res.data.fields) {
postData.append(key, res.data.fields[key]);
}
postData.append('file', image_name);
return axios.post(res.data.url, postData);
})
.then((res) => {
dispatch({
type: GET_PARTICULAR_BLOG_IMG_UPLOAD_AWS,
payload: res.data
});
})
.catch((err) => {
console.log(err)
});
};
I received the response from first axios request in frontend like below
signed_url: {
url: 'https://my-site.s3.amazonaws.com/',
fields: {
acl: 'public-read',
'Content-Type': 'image/jpeg',
key: 'xxxxxxxxxxx.jpg',
AWSAccessKeyId: 'xxx_access_id_xxxx',
policy: 'xxx_policy_xxx',
signature: 'xxx_signature_xxx'
}
},
url: 'https://my-site.s3.amazonaws.com/xxxxxxxxxxx.jpg'
},
here is settings.py
AWS_ACCESS_KEY_ID = os.environ.get('AWS_ACCESS_KEY_ID')
AWS_SECRET_ACCESS_KEY = os.environ.get('AWS_SECRET_ACCESS_KEY')
AWS_STORAGE_BUCKET_NAME = os.environ.get('AWS_STORAGE_BUCKET_NAME')
AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None
DEFAULT_FILE_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
I also ran into the same problem. Here is fix I got for you.
your backend django code should look like this.
import boto3
def create_presigned_url(
object_name,
object_type,
bucket_name=settings.BUCKET_NAME,
expiration=3600
):
# Generate a presigned S3 POST URL
s3_client = boto3.client(
's3',
aws_access_key_id=settings.AWS_ACCESS_KEY_ID,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_REGION_NAME,
)
response = s3_client.generate_presigned_url(
'put_object',
{
'Bucket': bucket_name,
'Key': object_name,
'ACL': 'public-read',
'ContentType': object_type
},
ExpiresIn=expiration,
)
return {
"signedRequest": response,
'url': f'https://{bucket_name}.s3.{settings.AWS_REGION_NAME}.amazonaws.com/{object_name}'
}
object name and the object type is the file name and file type contained in your request object.
The code at your frontend should be like this
import axios from "axios";
const fileHandler = (file) => {
let response = await getSignedUrl({
fileName: file.name,
fileType: file.type,
});
let { signedRequest, url } = response.data || {};
// fileType in the headers for the upload
let options = { headers: { "Content-Type": file.type } };
let instance = axios.create();
instance.put(signedRequest, file, options);
}
The url key from the response object is your image url after sending a put request to signedRequest
you can use the below library for the uploading image that will
https://django-storages.readthedocs.io/en/latest/backends/amazon-S3.html