V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
drymonfidelia
V2EX  ›  Python

这个场景下,在用户下载完转换好的文件再删除临时文件的最佳实际是什么?

  •  
  •   drymonfidelia · 117 天前 · 2633 次点击
    这是一个创建于 117 天前的主题,其中的信息可能已经有所发展或是发生改变。
    from flask import Flask, request, send_file, abort
    import ffmpeg
    import os
    import tempfile
    import time
    
    app = Flask(__name__)
    
    @app.route('/audio_appledevices/<name>.m4a', methods=['GET'])
    def convert_audio(name):
        ogg_path = f'./audio/{name}.ogg'
        if not os.path.exists(ogg_path):
            abort(404)
        with tempfile.NamedTemporaryFile(delete=False, suffix='.m4a') as temp_file:
            m4a_path = temp_file.name
        try:
            ffmpeg.input(ogg_path).output(m4a_path, codec='alac').run(overwrite_output=True)
            response = send_file(m4a_path, as_attachment=True, download_name=f'{name}.m4a', mimetype='audio/mp4')
            @response.call_on_close
            def cleanup():
                time.sleep(1)
                if os.path.exists(m4a_path):
                    os.remove(m4a_path)
            return response
        except ffmpeg.Error as e:
            abort(500)
    
    
    if __name__ == '__main__':
        app.run(host='127.0.0.1', port=3389)
    

    除非另开一个线程,不管怎么写临时文件都不能被自动删掉,要不就是 ffmpeg 写不进文件,因为临时文件被占用。 因为 ffmpeg 需要 seekable 的流,所以输出到 IO 流不可行。

    请先测试再回复!我让 GPT-4 写了几十个版本,没有一个能用。

    请先测试再回复!我让 GPT-4 写了几十个版本,没有一个能用。

    请先测试再回复!我让 GPT-4 写了几十个版本,没有一个能用。

    请先测试再回复!我让 GPT-4 写了几十个版本,没有一个能用。

    27 条回复    2024-07-30 22:37:09 +08:00
    esile
        1
    esile  
       117 天前 via Android
    当然异步队列处理+定时删除
    expy
        2
    expy  
       117 天前
    try except 一般在 finally 里清理吧。

    偏题一下,你这 ogg 转 alac ,ogg 是无损吗?
    ho121
        3
    ho121  
       117 天前 via Android
    用临时目录 tempfile.TemporaryDirectory ,所有临时文件写到这个目录里,任务结束后删掉整个目录
    sunnysab
        4
    sunnysab  
       117 天前 via Android
    猜想:tempfile 是基于操作系统临时文件功能实现的(原理是,Windows 下 CreateFile 有个参数指示,Linux 下有 open )。尝试把 delete 参数置为 True (默认值)。
    另外,有没有可能是共享写的问题,把打开方式设为 'r+b'。ffmpeg 需要处于同一用户中,这点是应该满足的,实在不行 strace 一下看看是哪一步无权限。

    捉个虫,标题中应该是“最佳实践”,而不是“最佳实际”。
    sazima
        5
    sazima  
       117 天前
    bytesio:

    ```python
    process = (
    ffmpeg
    .input(ogg_path)
    .output('pipe:', format='wav')
    .run_async(pipe_stdout=True, pipe_stderr=True)
    )
    out, err = process.communicate()
    io = BytesIO(out)

    # 创建响应并设置合适的头信息
    response = make_response(send_file(io, mimetype='application/octet-stream', as_attachment=True, download_name='example.mp3'))
    response.headers['Content-Disposition'] = 'attachment; filename=example.bin'
    return response
    ```
    sazima
        6
    sazima  
       117 天前
    更正:
    response.headers['Content-Disposition'] = 'attachment; filename=example.mp3'
    Haku
        7
    Haku  
       117 天前
    定时任务删除转换完后超过 x 天的文件。
    rsyjjsn
        8
    rsyjjsn  
       117 天前   ❤️ 4
    网友只是提供一个解决问题的思路给你,你咋老想着网友写好代码,测试通过,原封不动的回复给你呢?
    justNoBody
        9
    justNoBody  
       117 天前   ❤️ 3
    > 请先测试再回复!我让 GPT-4 写了几十个版本,没有一个能用。

    ???测试再回复???你给我 3000 美金我考虑一下
    0o0O0o0O0o
        10
    0o0O0o0O0o  
       117 天前
    你这天天提上班遇到的问题面向 V2EX 上班已经不满足了,还要回答的人自行测试。。。工资给大伙分一下吧
    lsk569937453
        11
    lsk569937453  
       117 天前
    笑死。。。。这是面向 V2EX 编程的社会实践吗?
    Hopetree
        12
    Hopetree  
       117 天前
    既然是临时文件,当然是规定好清理规则,定时任务定时清理 N 天前生成的文件啊,又简单又快速
    djasdjds
        13
    djasdjds  
       117 天前
    😅很难想象这是求问的态度
    Wh1t3zZ
        14
    Wh1t3zZ  
       117 天前   ❤️ 1
    伸手党还这么理直气壮?
    gwy15
        15
    gwy15  
       117 天前
    我欠你的
    deplives
        16
    deplives  
       117 天前
    原来 op 就是那个 CORS 和 CSRF 都分不清的人 https://v2ex.com/t/1057881
    deplives
        17
    deplives  
       117 天前

    原来是站长亲戚,怪不得这么理直气壮的伸手
    drymonfidelia
        18
    drymonfidelia  
    OP
       117 天前
    @expy 不是,所以要定时删除,ogg 源文件有几 TB
    @ho121 @sunnysab 这两种方案我都尝试了,执行到 return 那边就会因为文件被 flask 占用无法删除报 500 错误
    @sazima 需要 alac 的输出,alac 需要 seekable 的流
    @rsyjjsn
    @justNoBody
    @0o0O0o0O0o
    @lsk569937453
    @djasdjds
    @gwy15 这个问题远没有那么容易解决,我已经想到会有大量无效答案了,写一下试试能不能减少一些
    vituralfuture
        19
    vituralfuture  
       117 天前 via Android   ❤️ 1
    没能把答案喂给你真是抱歉
    drymonfidelia
        20
    drymonfidelia  
    OP
       117 天前
    @rsyjjsn
    @justNoBody
    @0o0O0o0O0o
    @lsk569937453
    @djasdjds
    @gwy15 只是讨论最佳实践,我已经用脏实现写了个 cron 定时 rm * 来交差了,知道最佳实践也不会比别人得到更多好处
    不会可以不回答,没必要只会阴阳怪气
    amlee
        21
    amlee  
       117 天前
    @drymonfidelia 不会可以不要问,没必要理直气壮
    tomczhen
        22
    tomczhen  
       117 天前
    写了,测了,但是不想发了。( doge
    tangtang369
        23
    tangtang369  
       117 天前
    这样好像可以删除掉
    ```
    from flask import Flask, request, send_file, abort,after_this_request
    import ffmpeg
    import os
    import tempfile
    import time

    app = Flask(__name__)

    @app.route('/audio_appledevices/<name>.m4a', methods=['GET'])
    def convert_audio(name):
    ogg_path = f'./audio/{name}.ogg'
    if not os.path.exists(ogg_path):
    abort(404)
    with tempfile.NamedTemporaryFile(delete=False, suffix='.m4a') as temp_file:
    m4a_path = temp_file.name
    try:
    ffmpeg.input(ogg_path).output(m4a_path, codec='alac').run(overwrite_output=True)
    response = send_file(m4a_path, as_attachment=True, download_name=f'{name}.m4a', mimetype='audio/mp4')
    print("run m4a_path",m4a_path)

    @after_this_request
    def cleanup(response):
    print("run cleanup")
    time.sleep(1)
    if os.path.exists(m4a_path):
    os.remove(m4a_path)
    return response
    return response
    except ffmpeg.Error as e:
    abort(500)


    if __name__ == '__main__':
    app.run(host='127.0.0.1', port=3389)
    ```
    djasdjds
        24
    djasdjds  
       117 天前
    @drymonfidelia #20 牛皮
    drymonfidelia
        25
    drymonfidelia  
    OP
       117 天前
    @tangtang369 还是没有删除掉,in cleanup
    os.remove(m4a_path)
    PermissionError: [WinError 32] The process cannot access the file because it is being used by another process
    tangtang369
        26
    tangtang369  
       116 天前
    @drymonfidelia #25 你是 windows 吗 这个我 mac 和 linux 测了没啥问题
    drymonfidelia
        27
    drymonfidelia  
    OP
       116 天前 via iPhone
    @tangtang369 是的,可能是因为 Windows 的问题
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   989 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 20:14 · PVG 04:14 · LAX 12:14 · JFK 15:14
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.