user1707389
user1707389

Reputation: 99

Getting error "ValueError: read of closed file" in Flask while saving image file

I have a flask app that is reading metadata from uploaded jpeg files. It then writes the metadata to a text file. I keep getting an error that I'm reading a closed file but it I know I'm opening the file.

I have a python script that successfully reads the metadata from all files in a folder and writes it to a file. But when I put that code into a flask application I get the error.

My flask app has this one python file:

app.py:

import PIL
import PIL.Image
import PIL.ExifTags
import pandas
import os
from os import path
import shutil
import json
import requests
import datetime
from datetime import datetime
from datetime import timedelta
from flask import Flask , render_template, request

app = Flask(__name__)

a = datetime(1970,1,1,0,1,1)

api_token = 'donotputactualkey'
api_url_base = 'https://api.darksky.net/forecast/'

APP_ROOT = os.path.dirname(os.path.abspath(__file__))

LOG_ROOT = os.path.join(APP_ROOT,'logs')
if not os.path.isdir(LOG_ROOT):
    os.mkdir(LOG_ROOT)


if not path.isfile('logs/ErrorLog.txt'):
    err = open('logs/ErrorLog.txt','a')
    err.write('FileName, ErrorDesc,DateTime \n')
    err.close()
else:
    err = open('logs/ErrorLog.txt','a')



if not path.isfile('logs/PhotoLatLong.txt'):
    log = open('logs/PhotoLatLong.txt','a')
    log.write('FileName,DateTaken,Lat,Long,precipIntensity,PrecipProbability,temperature,apparentTemperature,dewPoint,humidity,pressure,windSpeed,windGust,windBearing,CloudCover,uvIndex,visibility,nearest-station \n')
    log.close()
else:
    log = open('logs/PhotoLatLong.txt','a')


@app.route('/')
def index():
    return render_template('upload.html')

@app.route('/upload', methods = ['GET','POST'])
def upload():
    target = os.path.join(APP_ROOT,'images/')
    print(target)

    if not os.path.isdir(target):
        os.mkdir(target)
    CurDate = str(datetime.now()).split('.')[0]
    if err.closed:
        open(err)
    if log.closed:
        open(log)
    for file in request.files.getlist('file'):
        FileName = file.name
        print(FileName)
        img = PIL.Image.open(file)
        exif_data = img._getexif()
        if 306 not in exif_data.keys():
            print(str(File)+' has no datetime stamp')
            err.write(str(FileName+', No Date found,'+CurDate+'\n'))
            err.close()
        else:
            DateTaken =   exif_data[306]
            ApiDate = DateTaken[:4]+'-'+DateTaken[5:7]+'-'+DateTaken[8:10]+'T'+DateTaken[11:19]
            FileDate = DateTaken[:4]+'-'+DateTaken[5:7]+'-'+DateTaken[8:10]+' '+DateTaken[11:19]
            b = datetime(int(DateTaken[:4]),int(DateTaken[5:7]),int(DateTaken[8:10]),int(DateTaken[11:13]),int(DateTaken[14:16]),int(DateTaken[17:20]))
            newval = exif_data.get(34853)
        if 2 not in newval:
            err.write(str(str(FileName) +', GPS info not found,'+str(datetime.now()).split('.')[0]+'\n'))
            err.close()
        else:
            latdegs = exif_data[34853][2][0][0]
            latmins = exif_data[34853][2][1][0]
            latsecs = exif_data[34853][2][2][0]  / exif_data[34853][2][2][1]
            latcoords = latdegs + latmins/60 + latsecs/3600
            longdegs = exif_data[34853][4][0][0]
            longmins = exif_data[34853][4][1][0]
            longsecs = exif_data[34853][4][2][0]  / exif_data[34853][4][2][1]
            longcoords = longdegs + longmins/60 + longsecs/3600
            longcoords = longcoords * -1
            api_url_latlon = str(latcoords) +','+str(longcoords)
            time = ',' +ApiDate
            response = requests.get(api_url_base+api_token+'/'+api_url_latlon+time)
            dataapi = json.loads(response.content.decode('utf-8'))
            TT = (b-a).total_seconds()
            T1 = dataapi['hourly']['data'][0]['time']
            T2 = dataapi['hourly']['data'][1]['time']
            T3 = dataapi['hourly']['data'][2]['time']
            T4 = dataapi['hourly']['data'][3]['time']
            T5 = dataapi['hourly']['data'][4]['time']
            d = {0:TT-T1, 1:TT-T2, 2:TT-T3, 3:TT-T4, 4:TT-T5}
            k = min(d.items(),key=lambda x: x[1])
            pi = str(dataapi['hourly']['data'][k[0]]['precipIntensity'])
            pp = str(dataapi['hourly']['data'][k[0]]['precipProbability'])
            t = str(dataapi['hourly']['data'][k[0]]['temperature'])
            at = str(dataapi['hourly']['data'][k[0]]['apparentTemperature'])
            dp = str(dataapi['hourly']['data'][k[0]]['dewPoint'])
            hum = str(dataapi['hourly']['data'][k[0]]['humidity'])
            pr = str(dataapi['hourly']['data'][k[0]]['pressure'])
            ws = str(dataapi['hourly']['data'][k[0]]['windSpeed'])
            wg = str(dataapi['hourly']['data'][k[0]]['windGust'])
            wb = str(dataapi['hourly']['data'][k[0]]['windBearing'])
            cc = str(dataapi['hourly']['data'][k[0]]['cloudCover'])
            uv = str(dataapi['hourly']['data'][k[0]]['uvIndex'])
            vi = str(dataapi['hourly']['data'][k[0]]['visibility'])
            log.write(str(str(FileName) +','+str(FileDate)+','+str(latcoords)+','+str(longcoords)+','+pi+','+pp+','+t+','+at+','+dp+','+hum+','+pr+','+ws+','+wb+','+cc+','+uv+','+vi+'\n') )
            img.close()
            log.close()

        print(file)
        filename = file.filename
        destination = '/'.join([target, filename])
        print(destination)
        file.save(destination)

    return render_template('complete.html')


if __name__ == "__main__":
    app.run(debug = True)

Expect to be redirected to 'complete.html' but the console shows this error:

 * Serving Flask app "app" (lazy loading)
 * Environment: production
   WARNING: Do not use the development server in a production environment.
   Use a production WSGI server instead.
 * Debug mode: on
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 964-871-100
 * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
C:\Users\denjs\Documents\UdemyFlaskReview\PicUpload\images/
file
<FileStorage: 'P5040063.JPG' ('image/jpeg')>
C:\Users\denjs\Documents\UdemyFlaskReview\PicUpload\images//P5040063.JPG
127.0.0.1 - - [16/Aug/2019 13:39:02] "POST /upload HTTP/1.1" 500 -
Traceback (most recent call last):
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 2309, in __call__
    return self.wsgi_app(environ, start_response)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 2295, in wsgi_app
    response = self.handle_exception(e)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 1741, in handle_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\_compat.py", line 35, in reraise
    raise value
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 2292, in wsgi_app
    response = self.full_dispatch_request()
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 1815, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 1718, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\_compat.py", line 35, in reraise
    raise value
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 1813, in full_dispatch_request
    rv = self.dispatch_request()
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\flask\app.py", line 1799, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "C:\Users\denjs\Documents\UdemyFlaskReview\PicUpload\app.py", line 123, in upload
    file.save(destination)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\site-packages\werkzeug\datastructures.py", line 2728, in save
    copyfileobj(self.stream, dst, buffer_size)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\shutil.py", line 79, in copyfileobj
    buf = fsrc.read(length)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\tempfile.py", line 740, in read
    return self._file.read(*args)
  File "C:\Users\denjs\AppData\Local\conda\conda\envs\mynewflaskenv\lib\tempfile.py", line 485, in func_wrapper
    return func(*args, **kwargs)
ValueError: read of closed file
127.0.0.1 - - [16/Aug/2019 13:39:02] "GET /upload?__debugger__=yes&cmd=resource&f=style.css HTTP/1.1" 200 -
127.0.0.1 - - [16/Aug/2019 13:39:02] "GET /upload?__debugger__=yes&cmd=resource&f=debugger.js HTTP/1.1" 200 -
127.0.0.1 - - [16/Aug/2019 13:39:02] "GET /upload?__debugger__=yes&cmd=resource&f=jquery.js HTTP/1.1" 200 -
127.0.0.1 - - [16/Aug/2019 13:39:02] "GET /upload?__debugger__=yes&cmd=resource&f=console.png HTTP/1.1" 200 -
127.0.0.1 - - [16/Aug/2019 13:39:02] "GET /upload?__debugger__=yes&cmd=resource&f=ubuntu.ttf HTTP/1.1" 200 -

Upvotes: 2

Views: 4388

Answers (1)

Gino Mempin
Gino Mempin

Reputation: 29679

It's because you already closed the underlying file object with PIL:

for file in request.files.getlist('file'):
    FileName = file.name
    print(FileName)
    img = PIL.Image.open(file)
    ...
    else:
        ...
        img.close() # <------ 

    # At this point, the file object is already closed.
    file.save(destination)

Try removing all the image metadata processing codes in between getting file and file.save and it should succeed.

for file in request.files.getlist('file'):
    destination = '/'.join([target, filename])
    file.save(destination)

From the Image.close() docs:

Closes the file pointer, if possible.

This operation will destroy the image core and release its memory. The image data will be unusable afterward.

That affects the FileStorage object from request.files, which is in itself only a wrapper for the underlying file object.

Since it seems that the image file gets saved no matter what happens with the image metadata extraction inside the for loop, then just move it up to the start of the for loop.

for file in request.files.getlist('file'):
    filename = file.filename
    destination = '/'.join([target, filename])
    file.save(destination)

    FileName = file.name
    img = PIL.Image.open(file)
    # proceed with image data extraction
    ...

    # do not forget to close
    img.close() 

Upvotes: 2

Related Questions