1

I'm trying to upload an audio file from my iOS application to my flask backend. The POST request goes through, but I get an error saying "The browser (or proxy) sent a request that this server could not understand."

I was looking at the flask documentation to do this, but I can't see what they're doing thats different.

@auth.route('/uploadfile', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        print('post request received')
        file = request.files['file']
        if file and allowed_file(file.filename):
            print('file name is valid and saving')
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            return jsonify({'success': 1})
        else:
            print('file failed to save')
            return jsonify({'success': 2})


def allowed_file(filename):
    print('in allowed_file')
    return '.' in filename and \
        filename.rsplit('.', 1)[1] in app.config['AllOWED_EXTENSIONS']

//Config settings
UPLOAD_FOLDER = '/Users/Michael/Desktop/uploads'
ALLOWED_EXTENSIONS = set(['m4a'])

iOS side

func savePressed(){

    var stringVersion = recordingURL?.path
    let encodedSound = NSFileManager.defaultManager().contentsAtPath(stringVersion!)
    let encodedBase64Sound = encodedSound!.base64EncodedStringWithOptions(nil)
    let dict = ["file": encodedBase64Sound]        
    let urlString = "http://127.0.0.1:5000/auth/uploadfile"
    var request = NSMutableURLRequest(URL: NSURL(string: urlString)!)
    var session = NSURLSession.sharedSession()
    request.HTTPMethod = "Post"
    var err: NSError?
    request.HTTPBody = NSJSONSerialization.dataWithJSONObject(dict as NSDictionary, options: nil, error: &err)
    request.addValue("application/json", forHTTPHeaderField: "Content-Type")
    request.addValue("application/json", forHTTPHeaderField: "Accept")

    //Completion handler
    var task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
Brosef
  • 2,945
  • 6
  • 34
  • 69

1 Answers1

1

You are base64 encoding your file and posting it in a JSON object with application/json type. files is only populated when making a multipart/form-data type request.

Therefore, there is no data called "file" in request.files, but instead a base64 encoded string "file" in request.json. Flask raises an error when you try to access a key on the request collections that doesn't exist.

Your current way works, you can still decode the string back to binary and save it, but you'd need to generate your own filename or send it separately.

from base64 import b64decode
b64_data = request.json['file']  # the base64 encoded string
bin_data = base64decode(b64_data)  # decode it into bytes

# perhaps send the filename as part of the JSON as well, or make one up locally
filename = secure_filename(request.json['filename'])

with open(filename, 'wb') as f:
    f.write(bin_data)  # write the decoded bytes

The correct solution is to make the post as multipart/form-data and place the file in the file part of the body. I don't know how to do this in iOS, but it should definitely be supported. Here's what the post would look like with the Python requests library. If you can get the equivalent in iOS, then you don't have to change the Flask code.

with open(recording, 'rb') as f:
    requests.post('http://127.0.0.1:5000/auth/uploadfile', files={'file': f})
davidism
  • 121,510
  • 29
  • 395
  • 339