Error sending PDF from ReactJS to Flask app - reactjs

I'm trying to send a PDF from a frontend react app to a backend flask app using fetch. The idea is a user uploads a PDF on the frontend and sends to a backend that parses it and returns an edited version in JSON format.
Frontend:
function App() {
const [file, setFile] = useState(null);
const [data, setData] = useState();
const upload = (event) => {
setFile(event.target.files[0]);
};
const submit = () => {
const formData = new FormData();
formData.append("file", file);
fetch("http://localhost:5000/parser", {
method: "POST",
mode: "no-cors",
body: formData,
})
.then((res) => res.json())
.then((res) => {
setData(res);
console.log(res);
});
};
useEffect(() => {
if (file) {
submit();
}
}, [file]);
return (
<div className="App">
{" "}
<input type="file" accept="application/pdf" onChange={upload} />
<input type="submit" onClick={submit} />
</div>
);
}
export default App;
Backend:
app = Flask(__name__)
CORS(app)
#app.route("/parser", methods=['GET', 'POST'])
def parse():
file = request.files['file']
file.save(secure_filename("secure.pdf"))
fp = open("secure.pdf", 'rb')
data = convert(fp, 0)
response = make_response(data)
response.headers["Content-Type"] = "application/json"
response.headers["Access-Control-Allow-Origin"] = "http://localhost:5173"
return response
if __name__ == "__main__":
# for production change debug to False
app.run(debug=True)
The flask server is running with this error:
werkzeug.exceptions.BadRequestKeyError: 400 Bad Request: The browser (or proxy) sent a request that this server could not understand.
KeyError: 'file'
I'm not sure what I'm doing wrong here. As of now when I hit submit on the front end to send the PDF, the backend saves it to the local repo. So the PDF is getting through but for some reason there's an issue on the way back it seems.
Is there a better way to do this? To send a PDF and return a JSON? The python function I have works when I run it with a local PDF in the dir, but I need these two apps to work dynamically with each other.
The error makes even less sense to me because I'm able to save the file to the local repo, but am still getting an error seemingly on that line: file = request.files['file']

You cannot send the pdf as a json file until it get transformed. You should change it to b64 kind of transformation as temporary file(encoding and decoding) like this.

Related

Django API rejects file

I'm trying to send a csv file to my Django API and response with process data, but when I send it, I get the error:
django.utils.datastructures.MultiValueDictKeyError: 'file'
this is my react code:
import { useState } from 'react';
import './App.css';
function App() {
const [file, setFile] = useState(null);
const uploadFiles = e =>{
setFile(e);
}
const insertFile = async() => {
const f = new FormData();
f.append("file", file);
await fetch(
api,
{
method: 'POST',
headers: { 'content-type': 'multipart/form-data' },
body: f,
})
.then((response) => response.json())
.then((data)=>{
console.log(data);
})
.catch(error=>{
console.log(error);
});
}
return (
<>
<input type="file" name="file" onChange={(e)=>uploadFiles(e.target.files)}/>
<button onClick={()=>insertFile()}>Insertar</button>
</>
);
}
export default App;
And this is my view.py file that will process the information, for now, I just want to get the csv info in the frontend side, so the logic of how to process data doesn't matter right now.
#api_view(['POST'])
def eda(request):
file = request.FILES['file']
data = []
with open(file, encoding='utf-8') as csvf:
csvReader = csv.DictReader(csvf)
for rows in csvReader:
data.append(rows)
response = {
'csvData': data
}
return Response(response)
Seems like your files are not added in the FormData at all. It's because you are sending a list of files not a single file.
So instead of this
<input type="file" name="file" onChange={(e)=>uploadFiles(e.target.files)}/>
Use this
<input type="file" name="file" onChange={(e)=>uploadFiles(e.target.files[0])}/>
and on Django side use this:
file = request.FILES.get('file') # won't raise exception
if file is None:
# show some error response
Instead of request.FILES.get('file') try using request.data.get('file')

React + Django csv handle

I'm working on a Data Mining app with React and Django, I kind of understand how to send the file to Django, but how do I read the file, apply an algorithm and return the process data to react for showing it? The objective of the app is to read a differente csv file each time, so, I don't need to create models, don't even need to store the data, just handle the information.
I've seen a lot of tutorials, but everyone make use of a database, is there a method to process the file without saving anything, just processing and return the process data for create graphs and stuff? how an I do that?
This is my attempt with a react component for sending the file to django, but now, whats next? how do I read it in django? and how do I send the process data back to react?
import { useState } from "react";
function DragDropFiles(){
const [selectedFile, setSelectedFile] = useState();
const [isFilePicked, setIsFilePicked] = useState(false);
const changeHandler = (event) => {
setSelectedFile(event.target.files[0]);
setIsFilePicked(true);
}
const handleSubmission = async () =>{
const formData = new FormData();
formData.append('File', selectedFile);
let newData = await fetch(
base_url,
{
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data'
},
body: formData,
}
)
.then((response) => response.json())
.then((result) =>{
console.log('Success:', result);
})
.catch((error) =>{
console.log('Error:', error);
});
};
return(
<>
<div>
<input type="file" name="file" onChange={changeHandler} />
{isFilePicked ? (
<div>
<p>Filename: {selectedFile.name}</p>
<p>Filetype: {selectedFile.type}</p>
<p>Size in bytes: {selectedFile.size}</p>
</div>
) : (
<p>Select a file to show details</p>
)}
<div>
<button onClick={handleSubmission}>Submit</button>
</div>
</div>
</>
);
};
export default DragDropFiles;
I know that I have to import pandas, numpy and other libraries for handle data, but I mean, literally, how do I "receive" the csv file in the backend?
Thanks.

Upload file with React

I want to make a simple file upload form on the front end. Then, on the backend, I would pass the information about that file to an API.
Here is my front-end code where I call a specific function on the back end and pass the data:
import React from 'react';
import Axios from 'axios';
const Upload = () => {
// a local state to store the currently selected file.
const [selectedFile, setSelectedFile] = React.useState(null);
const handleSubmit = async (event) => {
event.preventDefault()
//Got all the Infos about my file
console.log(selectedFile)
const formData = new FormData();
formData.append("selectedFile", selectedFile);
//Empty result
console.log(formData)
Axios.get("http://localhost:3001/upload", {
//I will pass the data to a function in the backend
params: {
data: formData,
},
})
.then((Response) => {
console.log(Response)
})
.catch(function (error) {
console.log(error);
});
}
const handleFileSelect = (event) => {
setSelectedFile(event.target.files[0])
}
return (
<form onSubmit={handleSubmit}>
<input type="file" onChange={handleFileSelect}/>
<input type="submit" value="Upload File" />
</form>
)
};
export default Test
On the back-end side, a route call the method
router.get('/upload?', Upload);
Then finally the function in the backend to process
const ApiProcess = (req, res) => {
var axios = require('axios');
var data = req.query
console.log(req.query)
//All the API Stuff
}
But the problem is that I receive empty data in the Backend. What's wrong with my code?
Thanks
EDIT
On backend side I use multer and add 'app.use(multer().any())' on top of index file. That help cause now I cant access in backend to a simple formData. Now my function that receive the data log this '[Object: null prototype] {}'
Any idea ?
This is because your file is not getting forwarded from frontend
use FileReader instead
<input type="submit" value="Upload File" onChange={(e) =>
setFile(e.target.files)} />
const data = new FormData();
data.append(file[0])
and then you can access the file data on file[0] index and after storing the data you can forward it to the backend
there are some problems in your code.
first of all an upload request usually is a post type. and also you should send Content-Type header with your request. so:
Axios.post("http://localhost:3001/upload", formData {
headers: {
'Content-Type': 'Multipart/formData',
},
})
when you log formData it's always empty. you can use some methods like formData.keys() or formData.values() to see inside it.
Ok I got the solution. I missed a piece of middleware to process Multipart/formdata on Express Side :
const router = express.Router();
const multer = require("multer");
//Set the destination folder and Naming of the file that is upload
const storage = multer.diskStorage({
destination: function (req, file, cb) {
cb(null, 'uploads/')
},
filename: function (req, file, cb) {
cb(null, file.originalname)
}
})
const upload = multer({ storage: storage })
Then I process the formData with the files
router.post('/upload', upload.array("file"),Upload);
Thanks a lot for your help

POST 400 (BAD REQUEST) when uploading file between React and Flask

Keep getting a 400 error when uploading an excel file in my react front end and passing it to a flask api.
The flask backend route is as such:
def post(self):
name = request.form['name']
file = request.files['file']
(....do stuff with file and name)
The react API call is as follows:
export const uploadFile = async (file) => {
const data = new FormData()
data.append("file", file)
data.append("name", 'temp')
const api_url = "http://localhost:5000/uploadFile"
const settings = {
method: "POST",
body: data
}
try {
const response = await fetch(api_url, settings)
const result = await response.json()
if (result.message === 'OK') {
return result
}
} catch (error) {
return "Unable to load file"
}
}
Interesting the NAME field is getting received by Flask but the file object for some reason is not being processed by Flask.
I've tried adding a multi-part/formdata Content-Type in the React Fetch API call as well, but that doesn't help either.
Figured out the issue.
Turns out you need to pass the [0] following in the React call:
data.append("file", file[0])
I was trying to post an image file to Flask just using a vanilla JS fetch call, and #Anubhav's answer helped me realise that I needed to use FormData, rather than just posting the file/blob as the body. In case someone is looking for some full, working example code:
<input type="file" id="fileEl"/>
let formData = new FormData();
formData.append("file", fileEl.files[0]);
let response = await fetch("http://example.com/upload", {
method: "POST",
body: formData,
}).then(r => r.json());

How to send files to Django REST Framework from React?

I need to send arbitrary (e.g. xls) files from React-based frontend to Django REST Framework backend.
Googled and tried many code variants for couple of hours, none of them worked completely.
Here are essential parts of code:
React
1.1 Form input field
<input
type="file"
multiple={true}
accept=".xls,.xlsx,.csv,.txt"
onChange={this.handleFilesChosen}
/>
1.2 handleFilesChosen
handleFilesChosen = event => {
this.setState({
files: event.target.files
});
}
1.3 Upload click handler (authHeader is function substituting Authorization Bearer token)
handleUploadClick = event => {
let formData = new FormData();
for (let file of this.state.files) {
formData.append('files', file);
}
const csrf = this.getCookie('csrftoken');
fetch(`${API_BASE_PATH}/load-input-data/`, {
method: 'POST',
headers: authHeader({contentType: 'multipart/form-data', csrf: csrf}),
body: formData,
})
.then(result => result.json())
.catch(error => error);
}
DRF View
class LoadInputDataView(APIView):
parser_class = (MultiPartParser,)
#method_decorator(login_required)
def post(self, request, format=None):
print(request.data)
return Response(status=status.HTTP_201_CREATED)
I selected simple txt file (to make debugging easy, binary will go later) with hello world content, uploaded it and get <QueryDict: {}> in Django runserver console.
If I look at Chrome network tab, I see following empty request payload instead of real file content:
------WebKitFormBoundaryYw6ABRFkvxatzHqi
Content-Disposition: form-data; name="files"; filename="foo.txt"
Content-Type: text/plain
------WebKitFormBoundaryYw6ABRFkvxatzHqi--
Tried to remove contentType header - got 400 error with message JSON parse error (browser substitutes JSON contentType header automatically).
I'm stuck. Could anybody guide me?
Found solution. I should not set Content-Type header manually, it is set automatically with boundary option. Now Django's request.FILES work too and I could work with uploaded files from backend using code like:
class ParseInputDataView(APIView):
parser_class = (MultiPartParser,)
permission_classes = [permissions.IsAuthenticated]
def post(self, request, controller_id, format=None):
for file_entry in request.FILES.getlist('files'):
uploaded_file_name = file_entry.name
uploaded_file_content = file_entry.read()
...
I decided to maintain uniformity in the API and send the image within JSON.
In React:
const [image, setImage] = useState(null);
const handleImageChange = (e) => {
e.preventDefault();
const reader = new FileReader();
reader.onload = () => {
var blocks = reader.result.split(";");
const realData = blocks[1].split(",")[1];
setImage(realData);
};
reader.onerror = (error) => console.error(error);
reader.readAsDataURL(e.target.files[0]);
};
const onSaveHandler = () => {
fetch(`/url`, {
method: "post",
credentials: "include", // send cookie with auth
headers: {
"Content-Type": "application/json",
"X-CSRFToken": document.getElementById("csrf-token").value,
}
body: JSON.stringify({imageData: image}),
});
}
return(
<div>
<input
onChange={handleImageChange}
id="logo"
type="file"
multiple="false"
accept="image/*"
/>
<button onClick={onSaveHandler}>
SAVE
</button>
</div>);
In Django (DRF):
class CustomerViewSet(viewsets.ModelViewSet):
# override create method
def create(self, request, *args, **kwargs):
image_path = "whatever.jpg"
print('save image on disk: ' + image_path)
with open(image_path, "wb") as fh:
fh.write(base64.b64decode(request.data.get("imageData")))
return super().create(request)

Resources