如何使用 FastAPI 和 Jinja2 在 HTML 页面中显示上传的图像?

2025-03-06 08:52:00
admin
原创
84
摘要:问题描述:我正在使用 FastAPI 和 Jinja2 提供一个 HTML 页面来上传图像文件,然后打开另一个带有上传图像名称的 HTML 链接来显示该图像。尽管我能够使上传部分和上传图像链接正常工作,但我无法使标签src正常工作以实际显示图像。我该如何解决这个问题?这是main.py:from fastap...

问题描述:

我正在使用 FastAPI 和 Jinja2 提供一个 HTML 页面来上传图像文件,然后打开另一个带有上传图像名称的 HTML 链接来显示该图像。尽管我能够使上传部分和上传图像链接正常工作,但我无法使标签src正常工作以实际显示图像。我该如何解决这个问题?

这是main.py

from fastapi import FastAPI, File, UploadFile,Request
from fastapi.responses import HTMLResponse
from fastapi.staticfiles import StaticFiles 
from fastapi.templating import Jinja2Templates

app = FastAPI()
app.mount("/static", StaticFiles(directory="static"), name="static")
 """ upload file and save it to local """ 
@app.post("/upload-file")
async def upload_file(file: UploadFile = File(...)):
with open(file.filename, "wb") as f:
    f.write(file.file.read())
return {"filename": file.filename}

''' create html page to show uploaded file '''
@app.get("/")
async def root():
html_content = """
<html>
    <head>
        <title>Upload File</title>
    </head>
    <body>
        <h1>Upload File</h1>
        <form action="/upload-file/" method="post" enctype="multipart/form-data">
            <input type="file" name="file" />
            <input type="submit" />
        </form>
    </body>
</html>
"""
return HTMLResponse(content=html_content)


templates = Jinja2Templates(directory="templates")


 @app.get("/{filename}", response_class=HTMLResponse)
 async def read_item(request: Request, filename: str):
   return templates.TemplateResponse("template.html", {"request":  request,"filename": filename})

这是template.html

 <!DOCTYPE html>
<html>
<head>
    <title>HTML img Tag</title>
</head>

<body>
    <img src= "{{./filename}} " alt="uploaded image" width="400" height="400">
</body>
</html> 

我愿意尝试更好/更简单的方法来实现所需的结果。谢谢。


解决方案 1:

在同一个 HTML 页面中预览上传的图像

以下是评论部分中建议的解决方案的示例(之前已在此答案中描述),您可以使用它将FileReader.readAsDataURL()图像转换为客户端的 base64 编码字符串并在页面上显示图像,而无需 FastAPI 后端将其发送回给您,因为您需要显示用户上传的相同(未处理的)图像。 有关解决方案,请参见此处、此处以及此处和此处。 此外,要使用异步写入将图像文件写入磁盘,请查看此答案。

应用程序

from fastapi import File, UploadFile, Request, FastAPI, HTTPException
from fastapi.templating import Jinja2Templates

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.post("/upload")
def upload(file: UploadFile = File(...)):
    try:
        contents = file.file.read()
        with open("uploaded_" + file.filename, "wb") as f:
            f.write(contents)
    except Exception:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        file.file.close()
        
    return {"message": f"Successfuly uploaded {file.filename}"}

@app.get("/")
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})

模板/index.html

<script type="text/javascript">
    function previewFile() {
        const preview = document.querySelector('img');
        const file = document.querySelector('input[type=file]').files[0];
        const reader = new FileReader();
        reader.addEventListener("load", function() {
            preview.src = reader.result; // show image in <img> tag
            uploadFile(file)
        }, false);
        if (file) {
            reader.readAsDataURL(file);
        }
    }

    function uploadFile(file) {
        var formData = new FormData();
        formData.append('file', file);
        fetch('/upload', {
                method: 'POST',
                body: formData,
            })
            .then(response => {
                console.log(response);
            })
            .catch(error => {
                console.error(error);
            });
    }
</script>
<input type="file" onchange="previewFile()"><br>
<img src="" height="200" alt="Image preview...">

单击按钮即可上传图片

如果你需要在用户点击按钮后将图像上传到 FastAPI 服务器Upload Image(而不是像上面的模板那样在用户选择图像后立即自动上传),并且你想显示来自服务器的消息,无论图像是否已成功上传,你都可以使用下面的模板。

模板/index.html

<script type="text/javascript">
    function previewFile() {
        const preview = document.querySelector('img');
        var file = document.getElementById('fileInput').files[0];
        const reader = new FileReader();
        reader.addEventListener("load", function() {
            preview.src = reader.result; // show image in <img> tag
        }, false);
        if (file) {
            reader.readAsDataURL(file);
        }
    }

    function uploadFile(file) {
        var file = document.getElementById('fileInput').files[0];
        if (file) {
            var formData = new FormData();
            formData.append('file', file);
            fetch('/upload', {
                    method: 'POST',
                    body: formData,
                })
                .then(response => response.json())
                .then(data => {
                    document.getElementById("serverMsg").innerHTML = data.message;
                })
                .catch(error => {
                    console.error(error);
                });
        }
    }
</script>
<input type="file" id="fileInput" onchange="previewFile()"><br>
<input type="button" value="Upload Image" onclick="uploadFile()">
<p id="serverMsg"></p>
<img height="200">

在新标签页中预览图片

要在新选项卡而不是同一选项卡中预览图像,您可以使用以下命令。一旦用户单击按钮,下面的代码将在新选项卡中打开图像(使用此处"Upload Image"描述的方法) 。如果您需要在用户选择图像时打开选项卡,则注释掉函数previewFile()中的行uploadFile()并取消注释使用注释的 HTML<input type="file">元素onchange="previewFile()"

模板/index.html

<script type="text/javascript">
    function previewFile() {
       const preview = document.querySelector('img');
       var file = document.getElementById('fileInput').files[0];
       const reader = new FileReader();
       reader.addEventListener("load", function () {
          displayImgInNewTab(reader.result)
       }, false);
       if (file) {
          reader.readAsDataURL(file);
       }
    }

    function uploadFile() {
       var file = document.getElementById('fileInput').files[0];
       if (file) {
          var formData = new FormData();
          formData.append('file', file);
          fetch('/upload', {
                method: 'POST',
                body: formData,
             })
             .then(response => response.json())
             .then(data => {
                document.getElementById("serverMsg").innerHTML = data.message;
             })
             .catch(error => {
                console.error(error);
             });
          previewFile()
       }
    }

    function displayImgInNewTab(data) {
       var image = new Image();
       image.src = data
       var w = window.open("");
       w.document.write(image.outerHTML);
    }
</script>
<!--<input type="file" id="fileInput" onchange="previewFile()"><br>-->
<input type="file" id="fileInput"><br>
<input type="button" value="Upload Image" onclick="uploadFile()">
<p id="serverMsg"></p>
<img height="200">

在新的 Jinja2 模板中返回并显示上传的图像

如果您想要在新的 Jinja2 模板中显示上传的图像,则可以将图像转换为 base64 编码的字符串并使用 返回TemplateResponse,您可以在其中显示它。下面给出了工作示例。或者,您可以将上传的图像保存在目录下,并使用函数(例如)StaticFiles在新模板中将其显示给用户;但是 - 如本答案中所述,它演示了显示/下载从服务器返回的文件的另外两种方法 - 您需要考虑是否希望服务器为多个用户提供服务,以及用户是否应该能够查看/访问其他用户上传的图像,以及您可能需要考虑为文件名生成随机名称/UUID(因为用户可能会上传具有相同的图像),并有一种机制在不再需要时从磁盘中删除图像(类似于此答案)。在这种情况下,下面演示的方法可能是您的更好选择。url_for()`{{ url_for('static', path='/uploaded_img.png') }}`filename

应用程序

from fastapi import File, UploadFile, Request, FastAPI, HTTPException
from fastapi.templating import Jinja2Templates
import base64

app = FastAPI()
templates = Jinja2Templates(directory="templates")

@app.get("/")
def main(request: Request):
    return templates.TemplateResponse("index.html", {"request": request})
  
@app.post("/upload")
def upload(request: Request, file: UploadFile = File(...)):
    try:
        contents = file.file.read()
        with open("uploaded_" + file.filename, "wb") as f:
            f.write(contents)
    except Exception:
        raise HTTPException(status_code=500, detail='Something went wrong')
    finally:
        file.file.close()
        
    base64_encoded_image = base64.b64encode(contents).decode("utf-8")

    return templates.TemplateResponse("display.html", {"request": request,  "myImage": base64_encoded_image})

模板/index.html

<html>
   <body>
      <form method="post" action="/upload"  enctype="multipart/form-data">   
         <label for="file">Choose image to upload</label>
         <input type="file" id="files" name="file"><br> 
         <input type="submit" value="Upload">
      </form>
   </body>
</html>

模板/display.html

<html>
   <head>
      <title>Display Uploaded Image</title>
   </head>
   <body>
      <h1>My Image<h1>
      <img src="data:image/jpeg;base64,{{ myImage | safe }}">
   </body>
</html>

上述方法的替代方法是使用StaticFiles目录,您可以在其中保存用户上传的图像,然后返回一个TemplateResponse,将该图像的路径作为 Jinja2“上下文”(例如)中的键值对之一传递'imgPath': /static/uploaded_img.png',您可以使用它在中显示图像Jinja2Template,例如<img src="{{ imgPath }}">注意:使用此方法,/static使用系统的任何人都可以访问保存在目录下的图像。因此,如果这对您的任务来说是一个问题,最好不要采用这种方法。此外,使用此方法,您可能需要(根据项目的要求)设置一些流程以在一段有限的时间后删除图像,以防止耗尽磁盘空间。返回文件/图像的更多方法可以在此答案和此答案中看到。

相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   3878  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   2714  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Freshdesk、ClickUp、nTask、Hubstaff、Plutio、Productive、Targa、Bonsai、Wrike。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在项目管理过程中面临着诸多痛点,如任务分配不...
项目管理系统   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Monday、TeamGantt、Filestage、Chanty、Visor、Smartsheet、Productive、Quire、Planview。在当今快速变化的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多项目经理和团队在管理复杂项目时,常...
开源项目管理工具   49  
  本文介绍了以下10款项目管理软件工具:禅道项目管理软件、Smartsheet、GanttPRO、Backlog、Visor、ResourceGuru、Productive、Xebrio、Hive、Quire。在当今快节奏的商业环境中,项目管理已成为企业成功的关键因素之一。然而,许多企业在选择项目管理工具时常常面临困惑:...
项目管理系统   49  
热门文章
项目管理软件有哪些?
曾咪二维码

扫码咨询,免费领取项目管理大礼包!

云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用