Reputation: 99
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
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