FastAPI与Jinja2实现图片上传及显示教程

FastAPI与Jinja2实现图片上传及显示教程

本教程详细介绍了如何使用fastapi和jinja2框架实现图片上传功能,并在html页面中实时或通过服务器处理后显示图片。文章涵盖了客户端base64预览、服务器端base64编码传输以及使用静态文件服务等多种方法,并提供了相应的代码示例和注意事项,旨在帮助开发者构建高效安全的图片上传与展示系统。

一、引言:图片上传与显示的核心挑战

在Web开发中,用户上传图片并立即在页面上显示是一个常见需求。使用FastAPI作为后端框架,结合Jinja2模板引擎进行前端渲染时,实现这一功能需要考虑文件处理、数据传输以及前端渲染策略。初始尝试中,开发者可能会遇到直接使用文件名为FastAPI与Jinja2实现图片上传及显示教程标签的src属性无法正确显示图片的问题,这通常是由于浏览器无法直接访问服务器上未经暴露的文件路径所致。本教程将深入探讨几种有效的解决方案,并提供详细的代码示例。

二、客户端实时预览:利用Base64数据URL

最直接且用户体验最佳的图片预览方式是在客户端完成。用户选择图片后,浏览器可以在不向服务器发送文件的情况下,将图片数据转换为Base64编码的字符串,并将其作为FastAPI与Jinja2实现图片上传及显示教程标签的src属性值(即数据URL)进行显示。随后,再将图片文件上传至服务器。

2.1 FastAPI后端配置

后端FastAPI应用主要负责接收上传的图片文件并将其保存到服务器。

app.py

from fastapi import File, UploadFile, Request, FastAPI, HTTPExceptionfrom fastapi.templating import Jinja2Templatesimport osapp = FastAPI()templates = Jinja2Templates(directory="templates")# 确保上传目录存在UPLOAD_DIR = "uploaded_files"os.makedirs(UPLOAD_DIR, exist_ok=True)@app.post("/upload")async def upload_file(file: UploadFile = File(...)):    """    接收客户端上传的图片文件并保存到服务器。    """    try:        file_path = os.path.join(UPLOAD_DIR, file.filename)        # 异步写入文件,提高性能        contents = await file.read()        with open(file_path, "wb") as f:            f.write(contents)    except Exception as e:        raise HTTPException(status_code=500, detail=f'文件上传失败: {e}')    finally:        await file.close() # 确保文件句柄关闭    return {"message": f"文件 {file.filename} 上传成功!"}@app.get("/")async def main(request: Request):    """    渲染主页面,包含图片上传表单。    """    return templates.TemplateResponse("index.html", {"request": request})

注意事项:

os.makedirs(UPLOAD_DIR, exist_ok=True) 确保了文件保存目录的存在。await file.read() 和 await file.close() 是FastAPI中处理UploadFile的推荐异步方式,可以提高I/O效率。为了避免文件名冲突,实际应用中建议为上传的文件生成唯一的名称(如UUID)。

2.2 Jinja2前端模板 (index.html)

前端页面负责文件选择、客户端预览以及通过Fetch API将文件上传到后端。

2.2.1 自动预览并上传

这种方式在用户选择文件后立即进行预览和上传。

templates/index.html

    图片上传与预览    

上传图片


@@##@@

function previewAndUploadFile() { const preview = document.querySelector('img'); const fileInput = document.querySelector('input[type=file]'); const file = fileInput.files[0]; const serverMsg = document.getElementById('serverMsg'); serverMsg.innerHTML = ''; // 清空之前的消息 if (!file) { preview.src = ""; // 清空预览 return; } const reader = new FileReader(); reader.addEventListener("load", function() { // 将文件内容转换为Base64编码并显示在@@##@@标签中 preview.src = reader.result; uploadFile(file); // 预览完成后立即上传 }, false); reader.readAsDataURL(file); // 读取文件内容为数据URL } function uploadFile(file) { var formData = new FormData(); formData.append('file', file); // 将文件添加到FormData fetch('/upload', { method: 'POST', body: formData, // 发送FormData对象 }) .then(response => response.json()) .then(data => { document.getElementById("serverMsg").innerHTML = data.message; console.log(data); }) .catch(error => { document.getElementById("serverMsg").innerHTML = '上传失败: ' + error.message; console.error('上传失败:', error); }); }

2.2.2 点击按钮后上传

如果需要用户确认后再上传,可以添加一个上传按钮。

templates/index.html (修改部分)

    图片上传与预览    

上传图片


@@##@@ function previewFile() { const preview = document.querySelector('img'); const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const serverMsg = document.getElementById('serverMsg'); serverMsg.innerHTML = ''; // 清空之前的消息 if (!file) { preview.src = ""; // 清空预览 return; } const reader = new FileReader(); reader.addEventListener("load", function() { preview.src = reader.result; // 显示预览 }, false); reader.readAsDataURL(file); } function uploadFile() { const fileInput = document.getElementById('fileInput'); const file = fileInput.files[0]; const serverMsg = document.getElementById('serverMsg'); serverMsg.innerHTML = ''; // 清空之前的消息 if (!file) { serverMsg.innerHTML = '请先选择一个文件。'; return; } var formData = new FormData(); formData.append('file', file); fetch('/upload', { method: 'POST', body: formData, }) .then(response => response.json()) .then(data => { serverMsg.innerHTML = data.message; console.log(data); }) .catch(error => { serverMsg.innerHTML = '上传失败: ' + error.message; console.error('上传失败:', error); }); }

2.2.3 预览到新标签页

如果希望将预览图在新标签页中打开,可以在previewFile函数中调用一个新函数。

templates/index.html (修改部分)

    function previewFile() {       const fileInput = document.getElementById('fileInput');       var file = 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);    }

@@##@@

三、服务器端处理并显示:Base64编码或静态文件服务

有时,我们需要服务器在接收到图片后进行处理(例如缩放、水印),然后将处理后的图片返回给客户端显示。或者,出于某些原因,我们希望通过服务器来控制图片的显示。

3.1 方案一:服务器端Base64编码传输

这种方法将上传的图片在服务器端转换为Base64编码,然后通过Jinja2模板将编码后的字符串传递给前端,前端直接渲染Base64数据URL。

3.1.1 FastAPI后端配置

app.py

from fastapi import File, UploadFile, Request, FastAPI, HTTPExceptionfrom fastapi.templating import Jinja2Templatesimport base64import osapp = FastAPI()templates = Jinja2Templates(directory="templates")UPLOAD_DIR = "uploaded_files"os.makedirs(UPLOAD_DIR, exist_ok=True)@app.get("/")async def main(request: Request):    return templates.TemplateResponse("index.html", {"request": request})@app.post("/upload_and_display")async def upload_and_display(request: Request, file: UploadFile = File(...)):    """    接收文件,保存,然后将其Base64编码并返回到新的显示页面。    """    try:        contents = await file.read()        file_path = os.path.join(UPLOAD_DIR, file.filename)        with open(file_path, "wb") as f:            f.write(contents)    except Exception as e:        raise HTTPException(status_code=500, detail=f'文件处理失败: {e}')    finally:        await file.close()    # 将图片内容Base64编码    base64_encoded_image = base64.b64encode(contents).decode("utf-8")    # 渲染显示页面,并将Base64编码的图片字符串传递过去    return templates.TemplateResponse(        "display.html",         {"request": request, "myImage": base64_encoded_image, "filename": file.filename}    )

3.1.2 Jinja2前端模板

templates/index.html (上传表单)

         

选择图片上传



templates/display.html (显示页面)

         显示上传图片            

您上传的图片: {{ filename }}

@@##@@

返回上传页面

注意事项:

data:image/jpeg;base64,{{ myImage | safe }} 是Base64数据URL的正确格式。image/jpeg应根据实际图片类型调整,但通常浏览器能自动识别。| safe 过滤器用于告诉Jinja2该字符串是安全的HTML,不需要转义,否则等字符会被转义导致图片无法显示。Base64编码会使图片数据量增加约33%,对于大文件可能会影响传输和渲染性能。

3.2 方案二:静态文件服务

另一种常见的方法是将上传的图片保存到服务器的静态文件目录,然后通过静态文件服务来访问。

3.2.1 FastAPI后端配置

app.py

from fastapi import File, UploadFile, Request, FastAPI, HTTPExceptionfrom fastapi.responses import HTMLResponsefrom fastapi.staticfiles import StaticFilesfrom fastapi.templating import Jinja2Templatesimport osimport uuid # 用于生成唯一文件名app = FastAPI()# 挂载静态文件目录。上传的图片将保存在这里。STATIC_DIR = "static"UPLOADED_IMAGES_SUBDIR = "uploaded_images"os.makedirs(os.path.join(STATIC_DIR, UPLOADED_IMAGES_SUBDIR), exist_ok=True)app.mount(f"/{STATIC_DIR}", StaticFiles(directory=STATIC_DIR), name=STATIC_DIR)templates = Jinja2Templates(directory="templates")@app.get("/")async def main(request: Request):    return templates.TemplateResponse("index.html", {"request": request})@app.post("/upload_static")async def upload_static_file(request: Request, file: UploadFile = File(...)):    """    接收文件,保存到静态文件目录,然后重定向到显示页面。    """    try:        # 生成唯一文件名,避免冲突        file_extension = os.path.splitext(file.filename)[1]        unique_filename = f"{uuid.uuid4()}{file_extension}"        file_path = os.path.join(STATIC_DIR, UPLOADED_IMAGES_SUBDIR, unique_filename)        contents = await file.read()        with open(file_path, "wb") as f:            f.write(contents)    except Exception as e:        raise HTTPException(status_code=500, detail=f'文件上传失败: {e}')    finally:        await file.close()    # 构建静态文件URL    image_url = app.url_path_for(STATIC_DIR, path=f"{UPLOADED_IMAGES_SUBDIR}/{unique_filename}")    # 渲染显示页面,传递图片URL    return templates.TemplateResponse(        "display_static.html",         {"request": request, "image_url": image_url, "filename": file.filename}    )

3.2.2 Jinja2前端模板

templates/index.html (上传表单,修改action)

         

选择图片上传 (静态文件方式)



templates/display_static.html (显示页面)

         显示上传图片 (静态文件)            

您上传的图片: {{ filename }}

@@##@@

返回上传页面

注意事项:

app.mount(“/static”, StaticFiles(directory=”static”), name=”static”) 将static目录下的文件暴露为静态资源,可通过/static/filename.png访问。uuid.uuid4() 用于生成唯一文件名,避免不同用户上传同名文件时发生覆盖。app.url_path_for(STATIC_DIR, path=…) 是FastAPI中生成静态文件URL的推荐方式。安全与管理: 这种方法会将上传的图片公开暴露在 /static/uploaded_images 路径下。如果图片包含敏感信息或需要权限控制,此方法不适用。此外,需要考虑定期清理旧文件以避免磁盘空间耗尽。

四、总结与最佳实践

本教程详细介绍了在FastAPI和Jinja2环境中实现图片上传和显示的三种主要方法:客户端Base64预览、服务器端Base64编码传输和静态文件服务。

客户端Base64预览 提供最佳的用户体验,即时显示图片,无需服务器往返。适用于用户上传后立即查看原始图片。服务器端Base64编码传输 适用于服务器需要对图片进行处理(如压缩、加水印)后返回显示,且不希望将图片作为公开静态资源暴露的场景。但需注意Base64编码带来的数据量增大。静态文件服务 是传统且高效的方式,适用于图片无需特殊权限且可以公开访问的场景。但需要额外考虑文件名冲突、文件生命周期管理和安全性问题。

在实际项目中,选择哪种方法取决于具体需求:

即时预览:优先考虑客户端Base64预览。服务器处理后显示:如果图片需要服务器处理,且对文件大小不敏感,可选择服务器端Base64传输。公开图片或大量图片:如果图片可以公开访问,且需要高效的HTTP缓存,静态文件服务是更好的选择,但务必做好文件管理和安全措施。

无论选择哪种方案,以下几点都是值得注意的最佳实践:

异步文件操作:使用await file.read()和await file.close()进行异步文件读写,提高FastAPI应用的并发性能。唯一文件名:为上传的文件生成唯一的名称(如使用UUID),以防止文件名冲突和覆盖。文件类型验证:在前端和后端都进行文件类型和大小的验证,确保上传的是合法的图片文件。错误处理:完善的错误处理机制,向用户提供清晰的反馈。文件清理:对于服务器端存储的文件,考虑建立清理机制,定期删除不再需要的文件,避免占用过多磁盘空间。安全性:对于敏感图片,避免使用静态文件服务直接暴露,或实现严格的访问控制。

通过理解这些方法及其优缺点,开发者可以根据项目需求,灵活选择最合适的图片上传和显示方案。

图片预览...FastAPI与Jinja2实现图片上传及显示教程图片预览...FastAPI与Jinja2实现图片上传及显示教程Uploaded ImageUploaded Image

以上就是FastAPI与Jinja2实现图片上传及显示教程的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/1586945.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
如何在Web应用中实现动态字体大小与文本格式化
上一篇 2025年12月23日 03:00:15
前端数据持久化:修复localStorage加载时输入框内容丢失的常见错误
下一篇 2025年12月23日 03:00:21

相关推荐

  • 浏览器指纹的危害是什么呢 用户隐私泄露风险深度分析

    浏览器指纹通过收集设备和浏览器特征生成唯一标识,比Cookie更难清除,威胁隐私。它利用用户代理、屏幕分辨率、字体、Canvas渲染等信息构建“数字DNA”,即使清除Cookie、切换IP或使用隐身模式也难以摆脱。与传统Cookie不同,指纹追踪无需存储数据,而是基于设备固有属性,实现隐蔽、持久的跨…

    2026年8月30日
    200
  • 点击进入ao3网页版网站 ao3网页版无删减内容开放

    AO3通过非营利运营、社区驱动和精细内容标签系统实现内容开放,用户可利用分级与标签筛选无删减作品。 要访问AO3网页版并体验其无删减的丰富内容,最直接的方式就是通过其官方网址:archiveofourown.org。这是一个全球性的、由粉丝运营的非营利性网站,致力于提供一个开放、包容的创作和分享平台…

    2026年8月30日
    000
  • 悟空浏览器打开网页显示不安全_悟空浏览器“不安全”提示原因及解决

    悟空浏览器提示“不安全”通常因连接未加密或证书异常。首先检查网址是否为HTTPS,手动将http://改为https://并确认锁形图标显示;若支持HTTPS则可安全使用。其次查看SSL证书信息,确认有效期及域名匹配,避免访问证书过期或无效的网站。注意混合内容问题,即网页主连接为HTTPS但子资源通…

    2026年8月30日
    100
  • 如何自定义谷歌浏览器外观 Chrome浏览器主题皮肤安装教程

    安装Chrome主题可快速个性化浏览器外观,只需访问Chrome网上应用店,选择并点击“添加至Chrome”即可完成安装;2. 选择主题时应根据个人喜好筛选风格,并注意主题细节与兼容性;3. 高质量主题不会影响浏览器性能,但复杂主题可能轻微增加资源占用;4. 如需更换或恢复,可在设置中点击“重置为默…

    2026年8月30日
    100
  • win10放大镜功能怎么打开和关闭_Win10系统放大镜工具使用教程

    答案:可通过快捷键或设置开启放大镜。使用Win+加号快速启动并放大,Win+减号缩小,Win+Esc关闭;也可在设置-轻松使用中开启,并用快捷键或Ctrl+Alt+滚轮调整缩放级别。 如果您在使用电脑时感觉屏幕上的文字或图像过小,难以辨认,可以通过启用Windows 10内置的“放大镜”功能来放大显…

    2026年8月30日
    100
  • deepseek创始人是谁

    DeepSeeK的创始人梁文锋是人工智能领域的专家,拥有量化投资和机器学习技术背景。梁文锋于2002年考入浙江大学,2010年获硕士学位。在校期间,他带领团队使用机器学习进行量化交易。2013年,梁文锋与同学创立杭州幻方科技,专注于量化投资。2023年,梁文锋正式进军通用人工智能领域,创办了Deep…

    2026年8月30日
    100
  • win11怎么开启开发人员模式_Win11启用开发人员模式以安装和测试应用

    首先通过设置应用启用开发人员模式,进入隐私和安全性中的开发者选项并选择开发人员模式,确认警告后重启生效;专业版用户还可使用组策略编辑器,启用关闭应用程序兼容性引擎并隐藏警告;或通过注册表创建AppModelUnlock项,新建AllowDevelopmentWithoutDevLicense和All…

    2026年8月30日
    000
  • 麦芒40手机详细参数曝光:内置双AI 支持卫星通信

    中国电信官方此前宣布将推出新一代麦芒ai手机。最新消息显示,中国电信麦芒40手机上市暨星小辰终端智能体发布会定于6月25日14:30正式开启。cnmo了解到,已经有数码博主提前透露了麦芒40的详细参数信息。 麦芒40 据透露,麦芒40内置小艺与电信星小辰双AI系统,能够实时分析通话内容,有效防范电信…

    2026年8月30日
    000
  • Async/await和.then混合使用时,如何避免异步操作未完成就跳出循环?

    Async/await和.then并发执行的陷阱及解决方案 在使用async/await和.then处理异步任务时,常常会遇到一个棘手的问题:await后的代码执行完毕,async函数就提前返回,导致后续的.then代码未执行完成。本文将深入分析此问题,并提供有效的解决方案。 问题场景:假设父组件使…

    2026年8月30日
    000
  • 非Vue项目中,如何使用vue-quill-editor实现纯文本粘贴?

    非Vue项目中巧妙运用vue-quill-editor实现纯文本粘贴 在非Vue项目中使用vue-quill-editor时,如何将粘贴的富文本转换为纯文本并保持光标位置正确,是许多开发者面临的挑战。本文将详细讲解如何在普通JavaScript项目中,结合quill.js、vue-quill-edi…

    2026年8月30日
    100
  • win10怎么进入安全模式_win10进入安全模式多种方法

    通过系统设置、Shift+重启、msconfig、强制中断启动或安装介质五种方法可进入Windows 10安全模式,适用于不同故障场景,便于排查软件冲突、驱动问题或病毒影响。 如果您尝试在Windows 10系统中排查软件冲突、驱动问题或病毒影响,进入安全模式是一种有效的诊断手段。安全模式会以最小化…

    2026年8月30日
    200
  • 苹果三星下一代旗舰设计路线分化:iPhone变厚 S26变薄

    据最新爆料,将在2026年发布的两款旗舰手机——iphone 17 pro max和三星s26 ultra在设计理念上或将出现差异。消息称,苹果或将采用更厚的机身设计,而三星则可能进一步缩小体积,实现更加轻薄的外观。 当前在售的iPhone 16 Pro Max与S25 Ultra在尺寸和重量方面十…

    2026年8月30日
    000
  • win10剪贴板历史记录功能怎么开启_win10剪贴板历史记录开启与使用教程

    首先开启剪贴板历史记录功能,进入设置→系统→剪贴板,打开“剪贴板历史记录”开关;随后复制内容并按Win+V调出历史面板,点击任一记录即可粘贴;如需跨设备同步,应在同一Microsoft账户下启用“自动同步我复制的文本”;最后可通过Win+V面板删除单条或全部记录以管理隐私与资源。 如果您在使用Win…

    2026年8月30日
    100
  • 香香漫画网页入口全攻略 2025最新官网地址与安全访问指南

    香香漫画,一个致力于打造沉浸式漫画阅读体验的在线平台,以其海量的正版漫画资源、高清流畅的阅读体验和简洁友好的用户界面,吸引了无数漫画爱好者的目光。无论您热衷于何种题材,这里总能找到让您心动的内容。 2025年韩漫画免费版在线阅读全集☜☜☜点击进入 海内外漫画资源APP☜☜☜点击进入 入口一:官方网站…

    2026年8月30日
    000
  • win10桌面图标“我的电脑”怎么调出来_win10桌面显示“我的电脑”图标设置方法

    首先通过桌面图标设置恢复“我的电脑”:右键桌面→个性化→主题→桌面图标设置→勾选计算机→确定;其次可用控制面板:Win+R输入control→个性化→更改桌面图标→勾选计算机→确定;高级用户可通过注册表编辑器修改HKEY_CURRENT_USER路径下{20D04FE0-3AEA-1069-A2D8…

    2026年8月30日
    200
  • 专家调查结果显示:韩国半导体技术已经落后于中国

    韩国半导体技术落后于中国?最新调查结果引关注! ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 据韩联社2月23日报道,韩国科学技术企划评价院近期对39位专家进行的调查显示,韩国在部分关键半导体技术领域已落后于中国。 调查结果显示,中国在高集…

    2026年8月30日
    300
  • win10屏幕亮度调不了怎么办_win10屏幕亮度无法调节修复方法

    首先检查并更新显卡驱动,再启用PnP显示器,接着调整电源计划设置,运行系统疑难解答,最后通过修改注册表恢复亮度滑块功能。 如果您在使用Windows 10系统时发现屏幕亮度无法调节,可能是由于驱动程序异常、系统设置错误或电源管理功能失效导致。以下是多种修复此问题的方法: 本文运行环境:联想 Yoga…

    2026年8月30日
    100
  • 三星显示与英特尔合作 研发AI设备专用显示器

    ☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜ 三星显示与英特尔签署合作备忘录,致力于研发面向AI设备(例如AI PC)的先进显示屏。此举将助力英特尔提升移动平台性能,并为其GPU匹配更优显示方案;同时,三星有望进一步巩固其在高端笔记本电脑市…

    2026年8月30日
    100
  • win11无法访问共享文件夹怎么办_win11局域网共享访问失败解决方法

    首先启用网络发现和文件共享,确保设备可被识别并允许访问;接着通过组策略启用不安全的来宾登录以支持匿名连接;然后禁用SMB签名提升跨系统兼容性;再配置共享文件夹的共享权限与NTFS安全权限,添加everyone用户并赋予完全控制权;最后重启Workstation、Server等相关服务以恢复共享功能。…

    2026年8月30日
    100
  • 行内图片如何与文本基线完美对齐?

    行内图片与基线对齐的挑战 网页设计中,图片常被用作行内元素。然而,精确控制行内图片(特别是元素)与其父元素文本基线对齐却并非易事。本文通过一个案例分析,深入探讨行内图片的基线对齐机制以及margin属性的影响。 问题:图片与文本基线对齐失效 假设一个元素包含文本和一个元素,目标是使图片下边缘与文本下…

    2026年8月30日
    200

发表回复

登录后才能评论
关注微信