159

I am writing my first flask application. I am dealing with file uploads, and basically what I want is to read the data/content of the uploaded file without saving it and then print it on the resulting page. Yes, I am assuming that the user uploads a text file always.

Here is the simple upload function i am using:

@app.route('/upload/', methods=['GET', 'POST'])
def upload():
    if request.method == 'POST':
        file = request.files['file']
        if file:
            filename = secure_filename(file.filename)
            file.save(os.path.join(app.config['UPLOAD_FOLDER'], filename))
            a = 'file uploaded'

    return render_template('upload.html', data = a)

Right now, I am saving the file, but what I need is that 'a' variable to contain the content/data of the file .. any ideas?

Matt Hall
  • 7,614
  • 1
  • 23
  • 36
user2480542
  • 2,845
  • 4
  • 24
  • 25

8 Answers8

180

FileStorage contains stream field. This object must extend IO or file object, so it must contain read and other similar methods. FileStorage also extend stream field object attributes, so you can just use file.read() instead file.stream.read(). Also you can use save argument with dst parameter as StringIO or other IO or file object to copy FileStorage.stream to another IO or file object.

See documentation: http://flask.pocoo.org/docs/api/#flask.Request.files and http://werkzeug.pocoo.org/docs/datastructures/#werkzeug.datastructures.FileStorage.

tbicr
  • 24,790
  • 12
  • 81
  • 106
  • 3
    quick example: `file = request.files.get('file')` `filetype = magic.from_buffer(file.read(1024))` – endolith Dec 26 '14 at 20:00
  • 8
    hi @user2480542. I'm running into the same problem. Can you describe how you went about reading the contents of the client-uploaded file? I'm calling file.read() but am not getting anything. Thanks! – tmthyjames Feb 06 '15 at 21:21
  • 1
    @tmthyjames ```f = request.files['file']``` puts the uploaded file (in the request) to a var ("f"). Then ```f.read()``` then works using the above code. when is ```print f.read()``` I get correct looking junk in the terminal. Hope that helps. – Marc Feb 11 '15 at 17:56
  • 6
    If you are uploading a file and have a binary stream, you can easily convert it into a text stream by wrapping it in TextIOWrapper: `mystring = TextIOWrapper(binary_stream)` – Dutch Masters Jul 14 '16 at 14:57
  • With Flask 0.12 and python3 and utf-8 file you can do: flask_file = request.files['file'] file_lines = [line.decode("utf-8") for line in flask_file.file] – Aldo Canepa Feb 24 '17 at 14:54
  • 16
    `f.read()` didn't yield anything for me too. Calling first `f.seek(0)` did the trick for me. – w177us Dec 12 '17 at 20:28
  • What will be scope of the file/stream? – Thejesh PR Nov 13 '18 at 07:14
  • I am using postman to send file to flask api. then my flask api send file to 3rd party api. for example **r = requests.post(url, headers=headers, data=payload, files={'file': file.read()})** . I am getting issue at 3rd party api. 3rd party api says invalid image extension...I am sending same image direct to 3rd party api then it works...Help me. – Naisarg Parmar Jan 28 '19 at 09:00
  • I'm having the same trouble. If I send an image to Flask web service as raw binary body from Postman, I must read the jpg from request.data. But from an ajax browser client, passed as multi-part form data, I must read it in as request.files['key']. I notice that when coming from request.data, the payload size is different than when reading from form data (39080 vs 39041). The file is only read in correctly when it's from request.data. When read from form data, it appears to be malformed, and imageio cannot convert jpg->rgb. Assume that's your same problem. – Joey Carson Feb 03 '19 at 21:04
  • Saved into a file via stream s.t. `from werkzeug.datastructures import FileStorage; FileStorage(request.stream).save(os.path.join(app.config['UPLOAD_FOLDER'], filename))`. Working fine! +1 – gihanchanuka Feb 24 '19 at 21:27
  • to expand on @w177us answer take a look at [this](https://stackoverflow.com/a/16374481/7761401) why f.seek(0) is needed if f.read() returns an empty string –  Aug 18 '20 at 10:16
20

If you want to use standard Flask stuff - there's no way to avoid saving a temporary file if the uploaded file size is > 500kb. If it's smaller than 500kb - it will use "BytesIO", which stores the file content in memory, and if it's more than 500kb - it stores the contents in TemporaryFile() (as stated in the werkzeug documentation). In both cases your script will block until the entirety of uploaded file is received.

The easiest way to work around this that I have found is:

1) Create your own file-like IO class where you do all the processing of the incoming data

2) In your script, override Request class with your own:

class MyRequest( Request ):
  def _get_file_stream( self, total_content_length, content_type, filename=None, content_length=None ):
    return MyAwesomeIO( filename, 'w' )

3) Replace Flask's request_class with your own:

app.request_class = MyRequest

4) Go have some beer :)

Ciprian Tomoiagă
  • 3,773
  • 4
  • 41
  • 65
Dimitry Miles
  • 309
  • 2
  • 4
  • What is `MyAwesomeIO` in that code? – Superdooperhero Jul 21 '23 at 11:31
  • A file-like object, implementing all the standard file methods (open, close, read etc) In my case it’s a custom class, implementing all the standard methods, but using a remote data storage to store the data. This eliminates the need to store the temporary file locally before sending it to a remote storage solution. – dniq Jul 22 '23 at 18:37
7

I share my solution (assuming everything is already configured to connect to google bucket in flask)

from google.cloud import storage

@app.route('/upload/', methods=['POST'])
def upload():
    if request.method == 'POST':
        # FileStorage object wrapper
        file = request.files["file"]                    
        if file:
            os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = app.config['GOOGLE_APPLICATION_CREDENTIALS']
            bucket_name = "bucket_name" 
            storage_client = storage.Client()
            bucket = storage_client.bucket(bucket_name)
            # Upload file to Google Bucket
            blob = bucket.blob(file.filename) 
            blob.upload_from_string(file.read())

My post

Direct to Google Bucket in flask

jamartincelis
  • 127
  • 1
  • 5
3

I was trying to do the exact same thing, open a text file (a CSV for Pandas actually). Don't want to make a copy of it, just want to open it. The form-WTF has a nice file browser, but then it opens the file and makes a temporary file, which it presents as a memory stream. With a little work under the hood,

form = UploadForm() 
 if form.validate_on_submit(): 
      filename = secure_filename(form.fileContents.data.filename)  
      filestream =  form.fileContents.data 
      filestream.seek(0)
      ef = pd.read_csv( filestream  )
      sr = pd.DataFrame(ef)  
      return render_template('dataframe.html',tables=[sr.to_html(justify='center, classes='table table-bordered table-hover')],titles = [filename], form=form) 
TGanoe
  • 349
  • 1
  • 3
  • 10
3

I share my solution, using pandas


@app.route('/upload/', methods=['POST'])
def upload():
    if request.method == 'POST':
        # FileStorage object wrapper
        file = request.files["file"]                    
        if file:
            df = pd.read_excel(files_excel["file"])
Hoàng Lê
  • 141
  • 1
  • 1
  • request.files[“file”] is a temporary file on a local filesystem. It doesn’t solve the problem in the OP. – dniq Jul 22 '23 at 18:51
2

Building on a great answer by @tbicr the simplest form of that boils down to:

for line in request.files.get('file'):
   print("Next line: " + line)
piro91
  • 85
  • 1
  • 8
-2

in function

def handleUpload():
    if 'photo' in request.files:
        photo = request.files['photo']
        if photo.filename != '':      
            image = request.files['photo']  
            image_string = base64.b64encode(image.read())
            image_string = image_string.decode('utf-8')
            #use this to remove b'...' to get raw string
            return render_template('handleUpload.html',filestring = image_string)
    return render_template('upload.html')

in html file

<html>
<head>
    <title>Simple file upload using Python Flask</title>
</head>
<body>
    {% if filestring %}
      <h1>Raw image:</h1>
      <h1>{{filestring}}</h1>
      <img src="data:image/png;base64, {{filestring}}" alt="alternate" />.
    {% else %}
      <h1></h1>
    {% endif %}
</body>

-3

In case we want to dump the in memory file to disk. This code can be used

  if isinstanceof(obj,SpooledTemporaryFile):
    obj.rollover()

lalit
  • 5
  • 1
  • 1