How to decode the file uploaded in postman using python starlette - python

I have uploaded a .mp4 file in postman and now i need to read the file contents into a variable in python
This is my postman upload
This is my python file where i my request will be catched in this function
now how will be decode the file uploaded in this function
from starlette.requests import Request
from starlette.routing import Route, Mount
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse, FileResponse
class Post:
async def PostValuesToDb(requests: Request):
return PlainTextResponse("true")
routes = [
Route("/post", Post.PostValuesToDb,
name="PostValuesToDb", methods=["POST"]),
]
app = Starlette(
routes=routes
)
PostValuesToDb(requests: Request) captures the postman request.

as per the documentation
for the time being i am choosing to save the contents in a local directory, you can do as you wish from the contents variable
from starlette.requests import Request
from starlette.routing import Route, Mount
from starlette.applications import Starlette
from starlette.responses import PlainTextResponse, FileResponse
class Post:
async def PostValuesToDb(requests: Request):
form = await requests.form()
# print(dir(form)) # check what methods and attrs are available
# print(form.multi_items) # check all form fields submitted
filename = form["image"].filename # my particular form field
contents = await form["image"].read()
with open(f'./data/{filename}', 'wb') as uploaded_file:
uploaded_file.write(contents)
return PlainTextResponse("true")
routes = [
Route("/post", Post.PostValuesToDb,
name="PostValuesToDb", methods=["POST"]),
]
app = Starlette(
routes=routes
)

Related

Wrong specified media type using FastAPI

I'm trying to create a POST route where the media type would be set to "image/*" but when my route requires an argument, it's media type become "application/json" (even when I specify the response_class value).
Do you have an idea why?
Here is my source code:
from io import BytesIO
from fastapi.responses import ORJSONResponse
from fastapi import FastAPI
from fastapi.responses import StreamingResponse, JSONResponse, FileResponse
app = FastAPI(default_response_class=ORJSONResponse)
#app.post("/working", response_class=FileResponse)
async def send_w():
with open("/tmp/test/b.jpg", "rb") as f:
image = BytesIO(f.read())
image.seek(0)
return StreamingResponse(image, media_type="image/jpeg")
#app.post("/a", response_class=FileResponse)
async def send_a(a):
with open("/tmp/test/b.jpg", "rb") as f:
image = BytesIO(f.read())
image.seek(0)
return StreamingResponse(image, media_type="image/jpeg")
#app.post("/b", response_class=FileResponse)
async def send_b(b):
return "/tmp/test/b.jpg"
/a route:
You need to create function args to handle file type.
e.g.
from fastapi import FastAPI, File, UploadFile
#app.post("/image")
async def upload(data : UploadFile = File(...)):
image = await data.read()
# do your process here
read more:
https://fastapi.tiangolo.com/tutorial/request-files/

Using flask to run a python script

I have a script called output.py
This script takes in 2 inputs, fileA and file B.
I can run it on my terminal by using the command output.py -fileA -fileB. The script will create a new JSON file and save it to the directory.
I want to run this script using Flask. I've defined a bare bones App here but I'm not sure how I'd run this using Flask
from flask import Flask
import output
import scripting
app = Flask(__name__)
#app.route('/')
def script():
return output
if __name__ == '__main__':
app.run()
Can someone help me out here, thanks!
It appears you are new to Flask. Get some basic tutorials (there are many on the web).
There are a couple of options:
Send contents of file A and File b as json payload. Pull the A and B content off the json body and do the processing you need to and return the body.
Send the contents of the file as multipart/form-data (you can send multiple files).
Note: This is not working code - just for illustration.
from flask import Flask, request, make_response
app = Flask(__name__)
def build_response(status=False, error="", data={}, total=0, headers=[], contentType="application/json", expose_headers=["X-Total-Count"], retcode=400, additional_data=None):
resp = {"success": status, "error": error, "data": data}
resp = make_response(json.dumps(resp))
for item in headers:
resp.headers[item] = headers[item]
resp.headers['Content-Type'] = contentType
resp.headers.add('Access-Control-Expose-Headers', ','.join(expose_headers))
resp.status_code = retcode
return resp
#app.route('/run-script', methods=['POST'])
def run_script():
# check if the post request has the file part
try:
# Note: THis code is just to illustrate the concept.
# Option-1 (content type must be application/json)
json_dict = request.get_json()
fileA = json_dict["fileA"]
fileB = json_dict["fileB"]
# Option-2 (Note: fileA/fileB are objects, put a pdb and check it out)
fileA = request.files['fileA']
fileB = request.files['fileA']
resp = process(fileA, fileB)
return build_response(status=True, data=resp, retcode=200)
except Exception as e:
msg = f"Error - {str(ec)}"
return build_response(status=False, error=msg, retcode=400)

Fast API - how to show an image from POST in GET?

I'm creating an app using FastAPI that is supposed to generate resized version of uploaded images. The upload should be done through POST/images and after calling a path /images/800x400 it should show a random image from the database with 800x400 size.
I'm getting an error while trying to display an image.
from fastapi.responses import FileResponse
import uuid
app = FastAPI()
db = []
#app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):
contents = await file.read()
db.append(file)
with open(file.filename, "wb") as f:
f.write(contents)
return {"filename": file.filename}
#app.get("/images/")
async def show_image():
return db[0]
As a response I get:
{
"filename": "70188bdc-923c-4bd3-be15-8e71966cab31.jpg",
"content_type": "image/jpeg",
"file": {}
}
I would like to use: return FileResponse(some_file_path)
and in the file path put the filename from above. Is it right way of thinking?
First of all, you are adding the File object to your db list, that explains the response you get.
You want to write the content of the file to your db.
You also do not need to write it to the file system if you are using that as your "persistence" (of course all the files will go away if you shutdown or reload your app).
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import Response
import os
from random import randint
import uuid
app = FastAPI()
db = []
#app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):
file.filename = f"{uuid.uuid4()}.jpg"
contents = await file.read() # <-- Important!
db.append(contents)
return {"filename": file.filename}
#app.get("/images/")
async def read_random_file():
# get a random file from the image db
random_index = randint(0, len(db) - 1)
# return a response object directly as FileResponse expects a file-like object
# and StreamingResponse expects an iterator/generator
response = Response(content=db[random_index])
return response
If you want to actually save the files to disk this is the method I would use (a real db is still preferred for a full application)
from fastapi import FastAPI, File, UploadFile
from fastapi.responses import FileResponse
import os
from random import randint
import uuid
IMAGEDIR = "fastapi-images/"
app = FastAPI()
#app.post("/images/")
async def create_upload_file(file: UploadFile = File(...)):
file.filename = f"{uuid.uuid4()}.jpg"
contents = await file.read() # <-- Important!
# example of how you can save the file
with open(f"{IMAGEDIR}{file.filename}", "wb") as f:
f.write(contents)
return {"filename": file.filename}
#app.get("/images/")
async def read_random_file():
# get a random file from the image directory
files = os.listdir(IMAGEDIR)
random_index = randint(0, len(files) - 1)
path = f"{IMAGEDIR}{files[random_index]}"
# notice you can use FileResponse now because it expects a path
return FileResponse(path)
Reference:
(FastAPI inherits responses from Starlette)
Starlette Response
Starlette StreamingResponse
Starlette FileResponse
(Tiangolo's documentation is still very good to have)
FastAPI Response
FastAPI StreamingResponse
FastAPI FileResponse

How to test file upload in Django rest framework using PUT?

I would like to write a unit test for a view on a Django REST Framework application. The test should upload a file using PUT, in essence equivalent to
http -a malkarouri PUT http://localhost:8000/data-packages/upload/ka #tmp/hello.py
The code I have written so far is
factory = APIRequestFactory()
request = factory.put( '/data-packages/upload/ka',
data,
content_type='application/octet-stream',
content_disposition="attachment; filename=data.dump")
force_authenticate(request, user)
view = PackageView.as_view()
response = view(request, "k.py")
which, obviously, does not upload a file. The specific error when running the test is 400:
{u'detail': u'Missing filename. Request should include a Content-Disposition header with a filename parameter.'}
Notably, I am using a request factory to test the view rather than a full client. That is what makes solutions such as the one in this question not work for me.
What is the correct way to set the content disposition header?
Hi you need to use the SimpleUploadedFile wrapper for that :
from django.core.files.uploadedfile import SimpleUploadedFile
from django.core.files import File
data = File(open('path/bond-data.dump', 'rb'))
upload_file = SimpleUploadedFile('data.dump', data.read(),content_type='multipart/form-data')
request = factory.put( '/data-packages/upload/ka',
{'file':upload_file,other_params},
content_type='application/octet-stream',
content_disposition="attachment; filename=data.dump")
Ps : I am using APITestCase
from django.core.files.uploadedfile import SimpleUploadedFile
from django.test import TestCase
from rest_framework.test import APIClient
class MyTest(TestCase):
client_class = APIClient
def test_it(self):
file = SimpleUploadedFile("file.txt", b"abc", content_type="text/plain")
payload = {"file": file}
response = self.client.post("/some/api/path/", payload, format="multipart")
self.assertEqual(response.status_code, 201)
# If you do more calls in this method with the same file then seek to zero
file.seek(0)

send, receive, save local image file with requests / django rest framework

I'd like to http-send (e.g. requests.post) image files from servers/clients (with the requests lib) and receive/save these files with a django rest framework app. How do I do this?
Secondly I'd like to know how to extract parts from a QueryDict sent by requests.post in general. And more specific: How do I parse and save the _io-Object from this set of data?
# sending app
file = "path/to/image.jpg"
data = open(file, 'rb')
files = {
'json': (None, crawlingResultConnectionsJson, 'application/json'),
'file': (os.path.basename(file), open(file, 'rb'), 'application/octet-stream')
}
url = "http://127.0.0.1:8000/result/" # receiving/saving django rest framework app
r = requests.post(url, files=files)
I've tried quite a while now. Your help would be much appreciated! Thanks!
I came to a solution that perfectly fits my needs. As I only found contributions for either the sending OR the receiving part, I'll try to put everything together here.
Due to more flexibility my approach is to transfer json and images in seperated requests. The two following apps are completely independent.
The sending side does as follows (app with no server needed):
from django.core.serializers.json import DjangoJSONEncoder
import requests # http://docs.python-requests.org/en/master/
import datetime # in case...
import json
### send image stuff ###
urlImages = "http://127.0.0.1:8000/receive-images/"
file = "C:\\path\\to\\filename.jpg" # "\\" windows machine...
# this will probably run in a loop or so to process a bunch of images
with open(file, 'rb') as f:
filename = "filename.jpg"
files = {'file': (filename, f)}
r = requests.post(urlImages, files=files)
print(r) # some logging here
### send data stuff ###
data = data # python data - lists, dicts, whatever
json = json.dumps(data, cls=DjangoJSONEncoder) # DjangoJSONEncoder handles datetime fields
urlData = "http://127.0.0.1:8000/receive-data/"
headers = {'content-type': 'application/json'}
r = requests.post(urlData, json, headers=headers)
print(r) # some logging here
The receiving side needs to run a server (built-in django server for dev, apache with the WSGInterface in production) and it has this chum installed: http://www.django-rest-framework.org/
Finally we have two views to handle the requests:
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import status
from .api_controller import ApiController
from django.core.files.storage import default_storage
class ReceiveImages(APIView): # make sure to nail down corresponding url-confs
def post(self, request, format=None):
file = request.data.get('file')
filename = str(file)
with default_storage.open('images/' + filename, 'wb+') as destination:
for chunk in file.chunks():
destination.write(chunk)
return Response("ok", status=status.HTTP_200_OK)
class ReceiveData(APIView): # make sure to nail down corresponding url-confs
def post(self, request, format=None):
json = request.data
ApiController().createDataIfNotExists(json)
# As my json is quite complex,
# I've sourced out the DB-interactions to a controller-like class (coming
# from PHP symfony :)) with heavy use of the great
# MyModel.objects.get_or_create() method. Also the strptime() method is used
# to convert the datetime fields. This could also go here...
return Response("ok", status=status.HTTP_200_OK)
using chunk() in respect to https://stackoverflow.com/a/30195605/6522103
Please (!) comment/answer if you don't agree or think that this could be improved. Thanks!!

Categories

Resources