Nginx - WSGI - Flask - File upload is failing - file

I'm trying to upload files from my web app to my flask server that serves as API for the app.
As mentioned in the title I'm using Nginx as my webserver and running the flask app using WSGI.
This is the code in the webapp (React):
const formData = new FormData()
files.forEach((file) => {
formData.append("files", file)
})
console.log('files', files)
console.log('formData', formData)
axios.post('https://api.web.app/uploadfile', formData, {
headers: {
'Content-Type': 'multipart/form-data'
}
}).then(response => {
console.log('response.data.imgUrls', response.data.imgUrls)
if (DLOrSOW === 'sow') {
setSOWImgUrls(response.data.imgUrls)
} else {
setDLImgUrls(response.data.imgUrls)
}
})
}
This is the code on Flask:
#app.route('/uploadfile', methods=['POST'])
# #token_required
def upload_file():
print("Req is POST")
# check if the post request has the file part
if 'files' not in request.files:
print("No file found")
flash('No file')
return redirect(request.url)
files = request.files.getlist('files')
print(" files ", files)
filePaths = []
for file in files:
if file.filename == '':
flash('No selected file')
return redirect(request.url)
if file and allowed_file(file.filename):
filename = secure_filename(file.filename)
file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
print("url_for('download_file', name=filename)", url_for('download_file', name=filename))
filePaths.append(url_for('download_file', name=filename))
return jsonify({'imgUrls':filePaths })
Client(browser) respond in Console:
Request header field Authorization is not allowed by Access-Control-Allow-Headers.
[Error] XMLHttpRequest cannot load https://api.web.app/uploadfile due to access control checks.
On the other side in Flask logs I can see this request:
Oct 20 18:31:49 API uwsgi[1709272]: [pid: 1709272|app: 0|req: 36/81] 111.11.11.1 () {50 vars in 805 bytes} [Wed Oct 20 18:31:49 2021] OPTIONS /uploadfile => generated 6 bytes in 6 msecs (HTTP/1.1 200) 3 headers in 110 bytes (1 switches on core 0)
Just want to add that everything else works as it should. I'm able to do post, get, put etc requests to every other path in the Flask.
I think that the part that causes this is the use of "new FormData()"

In this case, for my situation, the problem was fixed by adding 'GET' parameter in the Flask path:
#app.route('/uploadfile', methods=['GET, 'POST'])
Apparently the order of the methods is important too

Related

REACT + DJANGO app, uploading excel files into the backend, Django returns empty dictionary

So I have been trying to upload a excel file from the frontend using a post request to a Django backend, however whatever I do the request[FILES] python dictionary is empty.
Does anyone have an idea of why this would happen?
This is my POST view from the Django views.py file
#api_view(["POST"])
#csrf_exempt
def processFile(request, *args, **kwargs):
data = request.data
print(data.items())
print(str(data.get('file0')))
if len(request.FILES) == 0 :
print("empty files")
print(request.FILES)
return Response("ok")
And now the way I am making the POST request in the frontend.
const fileList = async (actualFiles) => {
var dataForm = new FormData();
console.log(actualFiles)
for(let i=0;i<actualFiles.length;i++) {
const id = 'file' + i;
console.log("appending file" + i)
dataForm.append(id, actualFiles[i])
}
const res = await fetch(`http://localhost:8000/process-file/`,
{
method: 'POST',
headers: {
'Content-Type': 'multipart/form-data; boundary=----somefixedboundary'
},
body : dataForm,
})
const data = await res.json()
console.log("Data:" + data)
}
Worth mentioning: I have been trying for a while thinking the problem is in the request, however in the network tab on the browser I can see that it is all alright.
I am adding the content type and boundary because according to Django documentation if you do not add those the server will not be able to process the data.
Any ideas? Thank you in advance!

JavaScript CORS error when uploading files with Axios

I am developing a web application with Flask on the backend and React and Redux on the frontend.
I want to add a "Change Profile Picture" option to the profile page but whenever I make a post request with axios to my /api/user/upload_image/ route, i get the following errors:
Access to XMLHttpRequest at 'http://localhost:5000/api/user/update_image' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource.
PATCH http://localhost:5000/api/user/update_image net::ERR_FAILED
Which is weird becuase I have set up my CORS wrapper in my Flask app like so:
self.cors = CORS(self.app, resources={r"/api/*": {"origins": "*"}})
which should allow requests to /api/ from all origins.
I also tried to do the same thing with Postman and it worked like a charm - uploaded the file and saved it to /server/public/profile_pictures/
When i try to upload regular JSON text from my react application it works as well. It bugs out on file uploads only.
Here is the JSX for the input + the event handler
<label>
Change Profile Picture
<input onChange={(e) => {
this.setState({image: e.target.files[0]})}
} type="file" name="image" />
</label>
Then i have a submit button which dispatches the following action with this.state.image as a parameter:
export const updateImage = (file) => {
return async (dispatch, getState) => {
const formData = {
user_id: getState().currentUser.user.user_id,
auth_key: getState().currentUser.auth_key,
image: file
}
Axios.patch("http://localhost:5000/api/user/update_image", formData, {
headers: {
'Content-Type' : 'multipart/form-data'
}
})
.then(response => {
dispatch({type: UPDATE_IMAGE, payload: response.data})
})
}
I tried using the built in formData method to create the JS object too but that was no good either.
Finally here is the python method which is called when the /api/user/update_image route is hit:
def update_image(self, request):
image = request.files['image']
data = request.params
image.save("./public/profile_pictures/user_p_picture_id_"+data['user_id']+".jpg")
fsql.update("""UPDATE users SET profile_picture = %s WHERE user_id = %s""", ("/public/profile_pictures/user_p_picture_id_"+data['user_id']+".jpg", data['user_id']))
return jsonify({
"error_code" : "200",
"error_message" : "Success"
})
I actually solved this about a week and a half ago but I checked the status today.
So the solution was to make a few changes to my config parameter and CORS parameters. Here is the configs i am using right now:
config = {
'ORIGINS': [
'http://localhost:3000', # React
'http://127.0.0.1:3000', # React
],
'SECRET_KEY': '...' #secret key
self.cors = CORS(self.app, resources={
r'/api/*': {
"Access-Control-Allow-Origin": config["ORIGINS"],
"Access-Control-Allow-Credentials": True,
'supports_credentials': True
},
},
supports_credentials = True,
expose_headers = "*"
)
self.app.config['UPLOAD_FOLDER'] = r'/*' # Change this to only the folder you want to save images to
self.app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024 # Change this according to your file size
This solved my CORS and file transport issues.
I really hope this helps someone. The CORS docs on flask-cors do not cover everything in regards to file uploading and session storage so we kind of have to solve the errors without knowing how everything works - like trying to solve a puzzle with missing pieces.
HMU in messages if you have any good tools for CORS in flask which are well documented and have a community around them.

Download large files using React and AspnetCore to open browser save (headers)

I'm struggling with the problem of downloading large files using a web API in asp net core and frontend in React.
When the file starts downloading it doesn't show the dialog browser (Save As File Dialog) until it downloads the file in memory and next gives the possibility to save it.
When the file is bigger, like 200MB, the user can't choose where to save the file before start downloading it and see the download progress in the browser tab.
This is to use with react frontend and web API in aspnet core.
After reading for some hours I couldn't find the solution.
I can't use a link to download the file because I need to authenticate with a token.
Maybe I am missing any configuration or setup in my backend/frontend?
Any suggestions or advice will be very appreciated.
The method of my web api:
[HttpGet("{fileName}")]
[Authorize]
public IActionResult GetFile(string fileName)
{
Stream stream = _fileManager.GetFileContent(fileName);
// Response...
ContentDisposition cd = new ContentDisposition("attachment")
{
FileName = fileName,
// DispositionType = "attachment; filename=" + fileName + ";",
Inline = false // false = prompt the user for downloading; true = browser to try to show the file inline
};
Response.Headers.Add(HeaderNames.ContentDisposition, cd.ToString());
return File(stream, MediaTypeNames.Application.Zip);
}
The frontend using axios to get it.
async function Download() {
var authToken = await authService.Token();
console.log("calling request", process.env.REACT_APP_API_URL + "files/GetFile/iPro.zip");
if (authToken) {
BioAxios.get("/files/GetFile/file.zip", {
responseType: "blob",
headers: {
Authorization: "Bearer ".concat(authToken.access_token),
"Content-Type": "application/zip"
}
}).then(response => {
// Log somewhat to show that the browser actually exposes the custom HTTP header
console.log("Full Response", response);
// Let the user save the file.
FileSaver.saveAs(response.data, "file.zip");
});
}
}

How to fix: React fetch Post method failing using FormData with Flask API

I have a React web app that needs to communicate with a python Flask backend. I have my API sending data to the fetch method (GET) in React without a problem. I have used a fetch POST method to send data from React to python and I am getting the following error from my python script:
.
.
.
File "/Library/Python/2.7/site-packages/flask/app.py", line 1951, in full_dispatch_request
rv = self.handle_user_exception(e)
File "/Library/Python/2.7/site-packages/flask_cors/extension.py", line 161, in wrapped_function
return cors_after_request(app.make_response(f(*args, **kwargs)))
File "/Library/Python/2.7/site-packages/flask_api/app.py", line 96, in handle_user_exception
app_handlers = self.error_handler_spec[None].get(None, ())
KeyError: None
I have a server error as well: Origin http://localhost:3000 is not allowed by Access-Control-Allow-Origin. However since I have my GET method working without a problem I think this is a data form problem.
Here is my code in React:
onPageChanged = data => {
var payload = {
a: 1,
b: 2
};
var data = new FormData();
data.append( "json", JSON.stringify( payload ) );
fetch('http://127.0.0.1:5000/', {
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
firstParam: data
})
})
If I comment out the "body: ..." part of the code then I do not have any errors and the POST method in python is reached -- it definitely seems like this data is wrong, except that based on other question answers this form should be correct such as here.
This is my backend code for reference:
#app.route("/", methods=['POST'])
#cross_origin()
def search_api():
print "Search api reached!"
jsondata = request.get_json()
data = json.loads(jsondata)
print data
return "Search data recieved"

Getting a 403 (Forbidden) When Uploading to AWS S3 with Presigned Url

I'm getting an unexpected 403 with trying to upload a file to S3. The weird part is that I have accomplished this before when I did this using the Java AWS SDK to generate the presigned url. I am now using the Python AWS SDK to generate the presigned url and I feel like I am doing the exact same thing.
Here is my code that WORKS no problem:
public UploadSignedRequest getUploadSignedRequest() {
AmazonS3 s3Client = getS3Client();
// Set the pre-signed URL to expire after one hour.
Date expiration = DateUtil.getSignedUrlExpirationDate();
// Generate the pre-signed URL.
String objectKey = UUID.randomUUID().toString();
GeneratePresignedUrlRequest generatePresignedUrlRequest = new GeneratePresignedUrlRequest(BUCKET_NAME, objectKey)
.withMethod(HttpMethod.PUT)
.withExpiration(expiration);
String s3FilePath = String.format("%s/%s/%s", S3_URL, BUCKET_NAME, objectKey);
URL signedRequest = s3Client.generatePresignedUrl(generatePresignedUrlRequest);
return new UploadSignedRequest(signedRequest, s3FilePath, objectKey);
}
Here is the successful client code:
var config = {
onUploadProgress: function (progressEvent) {
var percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
updateProgressFunc(percentCompleted);
}
};
axios
.put(signedRequest.signedRequestUrl, videoFile, config)
.then(res => {
console.log(res);
console.log(res.status);
// save video metadata in db
dispatch(saveVideoMetadata(video));
})
.catch(function (error) {
console.log(error);
});
Now, here is me trying to accomplish essentially the same thing (image files instead of video files) using the Python AWS SDK.
def getS3UploadRequest(uuid):
return S3.generate_presigned_url(
ClientMethod='put_object',
Params={
'Bucket': BUCKET,
'Key': uuid
}
)
client code where I get the 403:
axios
.put(signedRequest, attachedFile)
.then(res => {
console.log("successfully uploaded file to s3");
console.log(res);
// dispatch(createProjectTaskComment(newComment, projectTaskId, userId, isFreelancer));
});
When I try to use the presignedUrl in postman, I get the following response back:
?xml version="1.0" encoding="UTF-8"?>
<Error><Code>SignatureDoesNotMatch</Code><Message>The request
signature we calculated does not match the signature you provided.
Check your key and signing method.</Message>
<AWSAccessKeyId>gibberish</AWSAccessKeyId><StringToSign>PUT
Thanks for the help!!!

Resources