Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django - post InMemoryUploadedFile to external REST api

In the Django Rest Framework I would like to post a file, received as an InMemoryUploadedFile, to a different server as soon as it is received.

It sounds simple, but the request.post() function does not seem to properly send over such a file :

def post(self, request, *args, **kwargs):
    data = request.data
    print(data)
    # <QueryDict: {'file': [<InMemoryUploadedFile: myfile.pdf (application/pdf)>]}>
    endpoint = OTHER_API_URL + "/endpoint"
    r = requests.post(endpoint, files=data)

My other server receives the request (through flask) with the name of the file, but not the content:

@app.route("/endpoint", methods=["POST"])
def endpoint():
    if flask.request.method == "POST":
        # I removed the many checks to simplify the code
        file = flask.request.files['file']
        path = os.path.join(UPLOAD_FOLDER, file.filename)
        file.save(path)        

        print(file) #<FileStorage: u'file.pdf' (None)>
        print(os.path.getsize(path)) #0

        return [{"response":"ok"}]

When posting a file directly to that api in form-data with postman, It works as expected:

        print(file) # <FileStorage: u'file.pdf' ('application/pdf')>
        print(os.path.getsize(path)) #8541

Any help on how to fix this, i.e. transform the InMemoryUploadedFile type in something a normal REST api can understand? Or maybe just adding the right headers?

like image 607
Robycool Avatar asked Jan 28 '23 19:01

Robycool


2 Answers

I had to figure this issue out passing an uploaded file from a Django front end website to a Django backend API in Python 3. The InMemoryUploadedFile's actual file data can be accessed via the object's .file property's .getvalue() method.

        path="path/to/api"
        in_memory_uploaded_file = request.FILES['my_file']
        io_file = in_memory_uploaded_file.file
        file_value = io_file.getvalue()
        files = {'my_file': file_value}
        make_http_request(path, files=files)

and can be shortened

        file = request.FILES['my_file'].file.getvalue()
        files = {'my_file': file}

Before this, trying to send InMemoryUploadFile objects, the file property, or the result of the read() method all proved to send a blank/empty file by the time it got to the API.

like image 151
jeffgamedev Avatar answered Jan 30 '23 10:01

jeffgamedev


I had the same problem and the same case. My working solution

    headers = {
        "Host": API_HOST,
        "cache-control": "no-cache",
    }

    try:
        data = json_request = request.POST['json_request'].strip()
        data = json.loads(data) # important!
    except:
        raise Http404

    try:
        in_memory_uploaded_file = request.FILES['file'].file.getvalue() 
        files = {'photo': in_memory_uploaded_file} # important!
    except:
        files = {}

    if USE_HTTPS:
        API_HOST = f'https://{API_HOST}'
    else:
        API_HOST = f'http://{API_HOST}'

    if authorization_key and len(authorization_key) > 0:
        response = requests.post(f'{API_HOST}/api/json/?authorization_key={authorization_key}', headers=headers, data=data, files=files)
    else:    
        response = requests.post(f'{API_HOST}/api/json/', headers=headers, data=data)

    json_response = json.dumps(response.json())
like image 44
Andrey Sobolev Avatar answered Jan 30 '23 09:01

Andrey Sobolev