我正在尝试将 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'
.
我不太确定如何继续!可能可以上传原始文件,然后让服务器在后台处理压缩(基于上传的文件),但这对于想要立即检索压缩版本以加载页面的客户端来说是一个问题。
最佳答案
在您的get_compressions
函数中,您正在读取原始file
,它是一个FileStorage对象,因此您的文件指针最终位于文件的末尾,并且您结束将零字节文件写入 S3。因此,您需要寻找
回到文件的开头,就像您对压缩版本所做的那样:
file.seek(0)
temp_thumb.seek(0)
temp_compress.seek(0)
关于python - Flask Web 服务器在处理和上传到 S3 时损坏图像,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54621777/