如何将 base64 编码的图像发送到 FastAPI 后端?

2025-02-11 09:51:00
admin
原创
64
摘要:问题描述:我正在使用此答案和该答案中的代码将 base64 编码的图像发送到 python FastAPI 后端。客户端如下所示:function toDataURL(src, callback, outputFormat) { var img = new Image(); ...

问题描述:

我正在使用此答案和该答案中的代码将 base64 编码的图像发送到 python FastAPI 后端。

客户端如下所示:

function toDataURL(src, callback, outputFormat) {
            var img = new Image();
            img.crossOrigin = 'Anonymous';
            img.onload = function() {
                var canvas = document.createElement('CANVAS');
                var ctx = canvas.getContext('2d');
                var dataURL;
                canvas.height = this.naturalHeight;
                canvas.width = this.naturalWidth;
                ctx.drawImage(this, 0, 0);
                dataURL = canvas.toDataURL(outputFormat);
                callback(dataURL);
            };
            img.src = src;
            if (img.complete || img.complete === undefined) {
                img.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///ywAAAAAAQABAAACAUwAOw==";
                img.src = src;
            }
        }

        function makeBlob(dataURL) {
            var BASE64_MARKER = ';base64,';
            if (dataURL.indexOf(BASE64_MARKER) == -1) {
                var parts = dataURL.split(',');
                var contentType = parts[0].split(':')[1];
                var raw = decodeURIComponent(parts[1]);
                return new Blob([raw], { type: contentType });
            }
            var parts = dataURL.split(BASE64_MARKER);
            var contentType = parts[0].split(':')[1];
            var raw = window.atob(parts[1]);
            var rawLength = raw.length;

            var uInt8Array = new Uint8Array(rawLength);

            for (var i = 0; i < rawLength; ++i) {
                uInt8Array[i] = raw.charCodeAt(i);
            }

            return new Blob([uInt8Array], { type: contentType });
        }

        ...

        toDataURL(
            images[0], // images is an array of paths to images
            function(dataUrl) {
                console.log('RESULT:', dataUrl);

                $.ajax({
                    url: "http://0.0.0.0:8000/check/",
                    type: 'POST',
                    processData: false,
                    contentType: 'application/octet-stream',
                    data: makeBlob(dataUrl)
                }).done(function(data) {console.log("success");}).fail(function() {console.log("error");});
            }
        );

服务器端如下:

@app.post("/check")
async def check(file: bytes = File(...)) -> Any:  
    // do something here

我只显示端点的签名,因为目前为止,其中没有发生太多事情。

下面是我按上面方法调用后端时的输出:

172.17.0.1:36464 - “选项/检查/HTTP/1.1” 200

172.17.0.1:36464 - “POST /检查/ HTTP/1.1” 307

172.17.0.1:36464 - “选项/检查 HTTP/1.1” 200

172.17.0.1:36464 - “POST /检查 HTTP/1.1” 422

简而言之,我不断收到422 error代码,这意味着我发送的内容与端点期望的内容不匹配,但即使读了一些资料,我仍然不清楚问题到底是什么。任何帮助都欢迎!


解决方案 1:

本答案所述,上传的文件将作为数据发送form。 根据FastAPI 文档:

application/x-www-form-urlencoded当表单中的数据不包含文件时,通常使用“媒体类型”进行编码

但是当表单包含文件时,它被编码为
multipart/form-data。如果你使用File,FastAPI 就会知道它必须从正文的正确部分获取文件。

无论您使用哪种类型,都bytes可以UploadFile,因为...

如果您将路径操作函数参数的类型声明为
bytes,则FastAPI 将为您读取文件,并且您将以字节形式接收内容。

因此,422 Unprocessable entity出现错误。在您的示例中,您发送二进制数据(使用application/octet-streamfor content-type),但是,您的 API 端点需要form数据(即multipart/form-data)。

选项 1

不要发送 base64 编码的图像,file而是使用 HTML (如此处form所示)或 Javascript (如下所示)按原样上传。正如其他人指出的那样,使用 JQuery 时,必须将 contentType 选项设置为 false 。使用Fetch API(如下所示),最好也将其省略,并强制浏览器设置它(以及强制性的多部分边界)。有关FastAPI 后端的读/写,请查看此答案。async

应用程序.py

import uvicorn
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>
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 => {
               console.log(response);
             })
             .catch(error => {
               console.error(error);
             });
    }
}
</script>
<input type="file" id="fileInput" name="file"><br>
<input type="button" value="Upload File" onclick="uploadFile()">

如果您想使用Axios库进行上传,请查看这个答案。

选项 2

如果您仍需要上传 base64 编码的图像,则可以使用data发送数据form,并在 API 端点中定义一个字段来接收数据。下面是一个完整的工作示例,其中发送了 base64 编码的图像,服务器接收该图像,对其进行解码并保存到磁盘。对于 base64 编码,客户端使用readAsDataURL方法。请注意,文件写入磁盘是使用同步写入完成的。在需要保存多个(或大)文件的场景中,最好使用写入,如此处所述。application/x-www-form-urlencoded`content-typeFormasync`

应用程序

from fastapi import Form, Request, FastAPI
from fastapi.templating import Jinja2Templates
import base64

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

@app.post("/upload")
def upload(filename: str = Form(...), filedata: str = Form(...)):
    image_as_bytes = str.encode(filedata)  # convert string to bytes
    img_recovered = base64.b64decode(image_as_bytes)  # decode base64string
    with open("uploaded_" + filename, "wb") as f:
        f.write(img_recovered)
    return {"message": f"Successfuly uploaded {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>
            base64String = reader.result.replace("data:", "").replace(/^.+,/, "");
            uploadFile(file.name, base64String)
        }, false);

        if (file) {
            reader.readAsDataURL(file);
        }
    }
    function uploadFile(filename, filedata){
        var formData = new FormData();
        formData.append("filename", filename);
        formData.append("filedata", filedata);
         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...">
相关推荐
  政府信创国产化的10大政策解读一、信创国产化的背景与意义信创国产化,即信息技术应用创新国产化,是当前中国信息技术领域的一个重要发展方向。其核心在于通过自主研发和创新,实现信息技术应用的自主可控,减少对外部技术的依赖,并规避潜在的技术制裁和风险。随着全球信息技术竞争的加剧,以及某些国家对中国在科技领域的打压,信创国产化显...
工程项目管理   1565  
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   1354  
  信创国产芯片作为信息技术创新的核心领域,对于推动国家自主可控生态建设具有至关重要的意义。在全球科技竞争日益激烈的背景下,实现信息技术的自主可控,摆脱对国外技术的依赖,已成为保障国家信息安全和产业可持续发展的关键。国产芯片作为信创产业的基石,其发展水平直接影响着整个信创生态的构建与完善。通过不断提升国产芯片的技术实力、产...
国产信创系统   21  
  信创生态建设旨在实现信息技术领域的自主创新和安全可控,涵盖了从硬件到软件的全产业链。随着数字化转型的加速,信创生态建设的重要性日益凸显,它不仅关乎国家的信息安全,更是推动产业升级和经济高质量发展的关键力量。然而,在推进信创生态建设的过程中,面临着诸多复杂且严峻的挑战,需要深入剖析并寻找切实可行的解决方案。技术创新难题技...
信创操作系统   27  
  信创产业作为国家信息技术创新发展的重要领域,对于保障国家信息安全、推动产业升级具有关键意义。而国产芯片作为信创产业的核心基石,其研发进展备受关注。在信创国产芯片的研发征程中,面临着诸多复杂且艰巨的难点,这些难点犹如一道道关卡,阻碍着国产芯片的快速发展。然而,科研人员和相关企业并未退缩,积极探索并提出了一系列切实可行的解...
国产化替代产品目录   28  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

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

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

内置subversion和git源码管理

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

免费试用