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

如何解决Chrome扩展传递图片至Python fastai后端的处理错误?

Chrome扩展图片拦截与FastAI后端格式传递问题

我正在开发一款Chrome扩展,用于拦截网站中包含特定内容的图片。目前在将图片以可被Python后端读取的格式传递时遇到问题,该后端需使用fastai模型分析图片并返回预测结果。尝试过base64编码、FormData传递、canvas处理等方式,但均未成功。

最新尝试的JavaScript函数

function DoElement() {
    if (showAll)
        return;
    let el = this;
    if (isImg(el)) {
        //attach load event - needed 1) as we need to catch it after it is switched for the blankImg, 2) in case the img gets changed to something else later
        DoLoadEventListener(el, true);
        //see if not yet loaded
        if (!el.complete) {
            //hide, to avoid flash until load event is handled
            MarkWizmaged(el, true);
            DoHidden(el, true);
            return;
        }
        let elWidth = el.width, elHeight = el.height;
        if (el.src == blankImg && !el.srcset) { //was successfully replaced
            DoHidden(el, false);
        }
        else if ((elWidth == 0 || elWidth > _settings.maxSafe) && (elHeight == 0 || elHeight > _settings.maxSafe)) { //needs to be hidden - we need to catch 0 too, as sometimes images start off as zero
            DoMouseEventListeners(el, true);
            if (!el.wzmHasTitleAndSizeSetup) {
                el.style.width = elWidth + 'px';
                el.style.height = elHeight + 'px';
                if (!el.title)
                    if (el.alt)
                        el.title = el.alt;
                    else {
                        el.src.match(/([-\w]+)(\.[\w]+)?$/i);
                        el.title = RegExp.$1;
                    }
                el.wzmHasTitleAndSizeSetup = true;
            }
            DoHidden(el, true);
            DoImgSrc(el, true);
            DoWizmageBG(el, true);
            el.src = blankImg;
            
            var canvas = document.createElement('canvas');
            var context = canvas.getContext('2d');
            canvas.width = el.width;
            canvas.height = el.height;
            context.drawImage(el, 0, 0);
            var imageData = context.getImageData(0, 0, el.width, el.height);

            // Create a Blob object from the image data
            var blob = new Blob([imageData.data.buffer], { type: 'image/png' });

            // Create a FormData object and append the blob to it
            var formData = new FormData();
            formData.append('image', blob, 'image.png');

            // Send the request to the server
            fetch('http://localhost:8000/predict', {
                method: 'POST',
                body: formData
            })
            .then((response) => response.json())
            .then((data) => {
                if (data.prediction === 'nudity') {
                    MarkWizmaged(el, true);
                    DoHidden(el, true);
                } else {
                    MarkWizmaged(el, false);
                    DoHidden(el, false);
                }
            })
            .catch((error) => {
                console.error('Error:', error);
            });
}

本地部署的Python后端代码

from fastai.vision.all import *
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse
import io
import base64

app = Starlette()
app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['*'], allow_methods=['*'])

# Load the trained model
learn = load_learner('/Users/.../Documents/Personal/WorkSafe/Backend/resnet18.pkl')

# Define the prediction route
@app.route("/predict", methods=["POST"])
async def predict(request):
    data = await request.form()
    image = data.get("image", None)

    if image is not None:
        # Retrieve the file data from the form
        image_data = await image.read()
        
        # Reset the stream position to the beginning
        image_data.seek(0)

        img = Image.open(io.BytesIO(image_data))
        img = img.convert('RGB')
        # Make the prediction
        prediction, _, _ = learn.predict(img)
        
        return JSONResponse({'prediction': str(prediction)})

运行错误信息

第一次错误

response = await func(request)
  File "/Users/shaulsaitowitz/Documents/Personal/WorkSafe/Backend/worksafe_app.py", line 26, in predict
    img = Image.open(io.BytesIO(image_data))
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/PIL/Image.py", line 2967, in open
    raise UnidentifiedImageError(
PIL.UnidentifiedImageError: cannot identify image file <_io.BytesIO object at 0x7fdff4b43ef0>

添加seek(0)后新错误

Traceback (most recent call last):
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/uvicorn/protocols/http/h11_impl.py", line 428, in run_asgi
    result = await app(  # type: ignore[func-returns-value]
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/uvicorn/middleware/proxy_headers.py", line 78, in __call__
    return await self.app(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/applications.py", line 122, in __call__
    await self.middleware_stack(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/errors.py", line 184, in __call__
    raise exc
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/errors.py", line 162, in __call__
    await self.app(scope, receive, _send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/cors.py", line 91, in __call__
    await self.simple_response(scope, receive, send, request_headers=headers)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/cors.py", line 146, in simple_response
    await self.app(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/middleware/exceptions.py", line 62, in __call__
    await wrap_app_handling_exceptions(self.app, conn)(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 57, in wrapped_app
    raise exc
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 46, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 727, in __call__
    await route.handle(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 285, in handle
    await self.app(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 74, in app
    await wrap_app_handling_exceptions(app, request)(scope, receive, send)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 57, in wrapped_app
    raise exc
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/_exception_handler.py", line 46, in wrapped_app
    await app(scope, receive, sender)
  File "/Users/shaulsaitowitz/opt/anaconda3/lib/python3.8/site-packages/starlette/routing.py", line 69, in app
    response = await func(request)
  File "/Users/shaulsaitowitz/Documents/Personal/WorkSafe/Backend/worksafe_app.py", line 27, in predict
    image_data.seek(0)

问题根源与解决方案

核心问题

  1. JavaScript端:
    • 绘制canvas前已将图片替换为空白图,导致传递的是空白数据
    • 直接用imageData.data.buffer创建Blob无法生成合法PNG文件(仅原始像素数据,无PNG文件头)
  2. Python端:
    • await image.read()返回bytes类型,无seek()方法,该调用完全多余

修正后的代码

修正后的JavaScript函数

function DoElement() {
    if (showAll)
        return;
    let el = this;
    if (isImg(el)) {
        DoLoadEventListener(el, true);
        if (!el.complete) {
            MarkWizmaged(el, true);
            DoHidden(el, true);
            return;
        }
        let elWidth = el.width, elHeight = el.height;
        if (el.src == blankImg && !el.srcset) {
            DoHidden(el, false);
        }
        else if ((elWidth == 0 || elWidth > _settings.maxSafe) && (elHeight == 0 || elHeight > _settings.maxSafe)) {
            DoMouseEventListeners(el, true);
            if (!el.wzmHasTitleAndSizeSetup) {
                el.style.width = elWidth + 'px';
                el.style.height = elHeight + 'px';
                if (!el.title)
                    if (el.alt)
                        el.title = el.alt;
                    else {
                        el.src.match(/([-\w]+)(\.[\w]+)?$/i);
                        el.title = RegExp.$1;
                    }
                el.wzmHasTitleAndSizeSetup = true;
            }
            // 保存原图片地址,避免绘制空白图
            const originalSrc = el.src;
            // 先隐藏图片,后续根据结果处理
            DoHidden(el, true);
            DoImgSrc(el, true);
            DoWizmageBG(el, true);

            var canvas = document.createElement('canvas');
            var context = canvas.getContext('2d');
            canvas.width = el.width;
            canvas.height = el.height;
            // 绘制原图片到canvas
            context.drawImage(el, 0, 0);
            
            // 使用canvas.toBlob生成合法PNG格式Blob
            canvas.toBlob(function(blob) {
                var formData = new FormData();
                formData.append('image', blob, 'image.png');

                fetch('http://localhost:8000/predict', {
                    method: 'POST',
                    body: formData
                })
                .then((response) => response.json())
                .then((data) => {
                    if (data.prediction === 'nudity') {
                        MarkWizmaged(el, true);
                        DoHidden(el, true);
                        el.src = blankImg; // 确认违规再替换为空白图
                    } else {
                        MarkWizmaged(el, false);
                        DoHidden(el, false);
                        el.src = originalSrc; // 恢复原图片
                    }
                })
                .catch((error) => {
                    console.error('Error:', error);
                    // 请求失败时恢复原图片
                    el.src = originalSrc;
                    DoHidden(el, false);
                });
            }, 'image/png');
        }
    }
}

修正后的Python后端代码

from fastai.vision.all import *
from starlette.applications import Starlette
from starlette.middleware.cors import CORSMiddleware
from starlette.responses import JSONResponse
import io

app = Starlette()
app.add_middleware(CORSMiddleware, allow_origins=['*'], allow_headers=['*'], allow_methods=['*'])

learn = load_learner('/Users/.../Documents/Personal/WorkSafe/Backend/resnet18.pkl')

@app.route("/predict", methods=["POST"])
async def predict(request):
    data = await request.form()
    image = data.get("image", None)

    if image is not None:
        image_data = await image.read()
        # 直接用bytes创建BytesIO,无需seek
        img = Image.open(io.BytesIO(image_data)).convert('RGB')
        prediction, _, _ = learn.predict(img)
        
        return JSONResponse({'prediction': str(prediction)})

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 04:42:05