我正在尝试从Flask Web服务器将图像存储在S3上。服务器接收图像并对其进行处理以创建两个副本(压缩+缩略图),然后上传所有三个副本。
成像的两个进程都很好,但是原始进程已损坏。该代码不会引发任何错误。
全部使用Python 3.6,Flask 1.0.2,Boto3 1.9.88
以下是上载页面代码的摘录:
form = UploadForm()
if form.validate_on_submit():
photo = form.photo.data
name, ext = os.path.splitext(form.photo.data.filename)
photo_comp, photo_thum = get_compressions(photo, 'JPEG')
pic_set = {}
pic_set['original'] = photo
pic_set['compressed'] = photo_comp
pic_set['thumbs'] = photo_thum
for pic in pic_set:
output = upload_to_s3(file=pic_set[pic], username=current_user.username, \
filetype = pic, \
bucket_name = current_app.config['S3_BUCKET'])
函数'get_compressions()'产生文件的.jpeg大小减小以及缩略图(抱歉,缩进格式出现错误):
def get_compressions(file, filetype):
#Creates new compressed and thumbnail copies. Checks for alpha
#channel, removes if present, resaves as compressed .jpeg, then
#wraps into a Werkzeug FileStorage type.
name, ext = os.path.splitext(file.filename)
temp_compress = BytesIO()
temp_thumb = BytesIO()
image = Image.open(file)
if image.mode in ['RGBA', 'LA', 'RGBa']:
image2 = Image.new('RGB', image.size, '#ffffff')
image2.paste(image, None, image)
image = image2.copy()
image.save(temp_compress, format=filetype, quality=85, optimize=True)
image.thumbnail((400,400), Image.ANTIALIAS)
image.save(temp_thumb, format=filetype, optimize=True)
temp_thumb.seek(0)
temp_compress.seek(0)
file_comp = FileStorage(stream=temp_compress,
filename=name + '.' + filetype,
content_type='image/jpg',
name=file.name,
)
file_thum = FileStorage(stream=temp_thumb,
filename=name + '.' + filetype,
content_type='image/jpg',
name=file.name,
)
return file_comp, file_thum
最后,'upload_to_s3()'函数在AWS S3上是非常简单的保存:
def upload_to_s3(file, username, filetype, bucket_name, acl= os.environ.get('AWS_DEFAULT_ACL')):
s3.upload_fileobj(
Fileobj=file
, Bucket=bucket_name
, Key = "{x}/{y}/{z}".format(x=username,y=filetype,z=file.filename)
, ExtraArgs = {'ContentType': file.content_type}
)
print('Upload successful: ', file.filename)
return file.filename
我相信压缩会影响原始文件对象的上传-虽然PIL image.save()返回一个新对象,但压缩操作似乎在某种程度上影响了原始对象。
在尝试对此进行研究时,我注意到Flask是标准的多线程工具,并且Python GIL不适用于I / O操作或图像处理-不知道这是否有意义。
我试图解决此问题的两个选择是:
更改代码顺序执行,使其执行原始上传-压缩-压缩上传,但这会导致错误'ValueError: I/O operation on a closed file'
在使用get_compressions()之前,先使用copy.deepcopy()创建一个新对象,但这会导致'TypeError: cannot serialize '_io.BufferedRandom' object'
。
我不太确定该如何进行!可能可以上传原始文件,然后在后台对服务器进行压缩(基于上传的文件),但这对想要立即检索压缩版本以加载页面的客户端来说是一个问题。
答案 0 :(得分:0)
在get_compressions
函数中,您正在读取原始的file
,它是一个FileStorage对象,因此您的文件指针最终位于文件的末尾,并且最终写入了一个零字节的文件到S3。因此,您需要seek
回到文件的开头,就像对压缩版本所做的那样:
file.seek(0)
temp_thumb.seek(0)
temp_compress.seek(0)