有没有更简洁的方法在通过Flask上传到S3前旋转智能手机图片?

2 投票
1 回答
1423 浏览
提问于 2025-04-18 10:57

我正在开发一个网页应用,它可以接收用户上传的图片,把这些图片存储在亚马逊的S3云存储上,然后把图片的链接存到一个SQLite数据库里。不过,问题是,手机拍的照片因为有EXIF标签,会导致图片显示旋转(因为这些图片是横向拍摄的,EXIF标签里有方向信息)。

现在,我的环境是从POST数据中获取文件,把它保存到我的静态文件夹里,如果需要的话,用PIL库旋转图片,然后上传到S3,最后删除本地的副本。下面是相关的代码:

from PIL import Image
import boto
from boto.s3.connection import S3Connection
from boto.s3.key import Key

def fix_orientation(filename):
    img = Image.open(filename)
    if hasattr(img, '_getexif'):
        exifdata = img._getexif()
        try:
            orientation = exifdata.get(274)
        except:
            # There was no EXIF Orientation Data
            orientation = 1
    else:
        orientation = 1

    if orientation is 1:    # Horizontal (normal)
        pass
    elif orientation is 2:  # Mirrored horizontal
        img = img.transpose(Image.FLIP_LEFT_RIGHT)
    elif orientation is 3:  # Rotated 180
        img = img.rotate(180)
    elif orientation is 4:  # Mirrored vertical
        img = img.rotate(180).transpose(Image.FLIP_LEFT_RIGHT)
    elif orientation is 5:  # Mirrored horizontal then rotated 90 CCW
        img = img.rotate(-90).transpose(Image.FLIP_LEFT_RIGHT)
    elif orientation is 6:  # Rotated 90 CCW
        img = img.rotate(-90)
    elif orientation is 7:  # Mirrored horizontal then rotated 90 CW
        img = img.rotate(90).transpose(Image.FLIP_LEFT_RIGHT)
    elif orientation is 8:  # Rotated 90 CW
        img = img.rotate(90)

    #save the result and overwrite the originally uploaded image
    img.save(filename)

def push_to_s3(**kwargs):
    try:
        conn = S3Connection(app.config["S3_KEY"], app.config["S3_SECRET"])
        buckets = [bucket.name for bucket in conn.get_all_buckets()]
        bucket = conn.get_bucket(app.config["S3_BUCKET"])

        k = Key(bucket)
        k.key = app.config["S3_UPLOAD_DIR"] + kwargs.get("filename")
        k.set_contents_from_filename(kwargs.get("photo"))
        k.make_public()
        return k
except Exception, e:
    abort(500)

这里是处理POST数据的部分

# Retrieving Form POST Data
fi = request.files.get("file")

#print "Storing and Rotating File (if needed)"
f = photos.save(fi)
path = photos.path(f)
fix_orientation(path)

#print "Uploading to S3"
img = push_to_s3(photo=path, filename=filename)

#print "Deleting Local Version"
os.remove(path)

上面的解决方案在Heroku的服务器上可以正常工作,但我觉得这个方法有点像是用胶带拼凑起来的解决方案。有没有更简单的方法来实现我现在的操作?也就是说,直接在内存中旋转上传的文件,然后再上传到S3?

我还在使用Flask-Uploads来处理上传图片的存储。

1 个回答

3

说实话,Pillow这个库支持很多种输入方式,不仅仅是文件名。比如,它可以接收bytearray(字节数组)、buffer(缓冲区)和file-like object(类似文件的对象)。第三种方式可能正是你需要的,因为从request.files中获取的内容其实就是一个FileStorage类型的类似文件的对象。这样一来,加载和转换的代码就简单多了:

def fix_orientation(file_like_object):
    img = Image.open(filename)

    # ... snip ...

    data = BytesIO()
    img.save(data)
    return data

由于我们要处理的数据不太依赖文件系统,我们还可以改用boto.s3.key.Keyset_contents_from_file方法,而不是set_contents_from_filename

def push_to_s3(photo, filename):
    # ... snip ...
    k.set_contents_from_file(photo, rewind=True)
    # ... etc. ...

这样一来,最终的实现就更加简单了:

# Retrieving Form POST Data
fi = request.files.get("file")

# print "Rotating File (if needed)"
fi = fix_orientation(fi)

# print "Uploading to S3"
push_to_s3(photo=fi, filename=filename)

撰写回答