python - Flask Web 服务器在处理和上传到 S3 时损坏图像
问题描述
我正在尝试将图像从 Flask 网络服务器存储在 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
函数中,您正在读取作为 FileStorage 对象的原始file
文件,因此您的文件指针最终位于文件末尾,并且您最终将零字节文件写入 S3。因此,您需要seek
回到文件的开头,就像您对压缩版本所做的那样:
file.seek(0)
temp_thumb.seek(0)
temp_compress.seek(0)
推荐阅读
- c - 为什么这个宏编译器依赖?
- selenium-webdriver - @Test 未执行 - 如果我在测试 xml 文件中添加 TestNG 侦听器,我的 @Test 方法未执行
- python - 我怎样才能让我的 opencv wwebcam 显示画面更快?
- c++ - 不同“种类”成员变量之间的区别
- python - 减去 2 个数据框列并得到没有奇怪舍入的结果(浮点运算)
- c# - 错误消息为 :Assert.AreEqual 失败。预期:<1>。实际:<0>
- java - Spring Web 控制器如何确定重定向的 HTTP 状态代码
- ruby-on-rails - 为什么 Rails 没有响应错误 406?
- android - Android Kotlin 中的 RecyclerView 效果选定项
- python - 输入一个数字 N,然后程序继续输出所有小于 N 的 2 的幂。我该如何继续?