You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从内存向FastAPI端点返回动态GIF?

如何用FastAPI返回带动效的GIF文件?

我尝试从内存向FastAPI端点发送GIF,能成功传输但GIF没有动效;如果把GIF保存到本地,动效是正常的。希望不保存图片,仅在内存中处理后返回给端点,试过相关帖子的方法但没解决问题。

我尝试的代码

main.py

@app.get('/youtube/{video_id}.gif')
def youtube_thumbnail (video_id: str, width: int = 320, height: int = 180):
    image = util.create_youtube_gif_thumbnail(video_id)
    return util.gif_to_streaming_response(image)

util.py

def gif_to_streaming_response(image: Image):
    imgio = BytesIO()
    image.save(imgio, 'GIF')
    imgio.seek(0)
    return StreamingResponse(content=imgio, media_type="image/gif")


def create_gif(images, duration):
    gif = images[0]
    output = BytesIO() # 用这个时显示静态图,但保存到本地文件就有动效
    # output = "./file.gif" 这个是正常的
    gif.save(output, format='GIF', save_all=True, append_images=images, loop=0, duration=duration)
    return gif


def create_youtube_gif_thumbnail(video_id: str):
    images = [read_img_from_url(f"https://i.ytimg.com/vi/{video_id}/{i}.jpg") for i in range(1,3)]
    return create_gif(images, 150)

问题原因

代码逻辑存在两个核心问题:

  1. create_gif里虽然用save_all=True生成了动画GIF,但最后返回的是单张images[0]的Image对象,并没有把生成的动画内容传递出去
  2. gif_to_streaming_response接收单张Image后,又单独保存成了静态GIF,自然没有动效

修复方案

修改代码逻辑,直接在内存中生成完整的动画GIF字节流,然后返回给FastAPI响应:

修改后的util.py

from io import BytesIO
from fastapi import StreamingResponse
from PIL import Image

def gif_to_streaming_response(gif_bytes: BytesIO):
    return StreamingResponse(content=gif_bytes, media_type="image/gif")


def create_gif(images, duration):
    output = BytesIO()
    # 基于第一张图保存所有帧,注意append_images从第二张开始避免重复
    images[0].save(
        output,
        format='GIF',
        save_all=True,
        append_images=images[1:],
        loop=0,
        duration=duration,
        disposal=2  # 关键参数:确保每一帧替换上一帧,避免帧残留
    )
    output.seek(0)
    return output


def create_youtube_gif_thumbnail(video_id: str):
    images = [read_img_from_url(f"https://i.ytimg.com/vi/{video_id}/{i}.jpg") for i in range(1,3)]
    return create_gif(images, 150)

修改后的main.py

@app.get('/youtube/{video_id}.gif')
def youtube_thumbnail(video_id: str, width: int = 320, height: int = 180):
    gif_bytes = util.create_youtube_gif_thumbnail(video_id)
    return util.gif_to_streaming_response(gif_bytes)

关键说明

  • create_gif现在直接返回包含完整动画的BytesIO对象,不再返回单张图片
  • append_images=images[1:]避免重复添加第一张图,减少冗余
  • disposal=2是PIL生成动画GIF的必要参数,它告诉GIF播放器在显示下一帧前清除当前帧,否则会出现帧叠加导致动效异常
  • 响应直接使用生成好的动画字节流,不需要二次保存,保证动效完整

内容的提问来源于stack exchange,提问作者Snailedlt

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 10:45:19