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

Flask多图上传删除后提交异常:NS_binding aborted错误排查

Flask多图上传问题修复:仅获取最后一张图片及NS_binding aborted错误解决

问题概述

开发网站时,用户可多次上传、删除图片,通过uploadedPhotos数组维护图片列表,但提交表单后后端仅能获取最后一次上传的图片,且浏览器出现NS_binding aborted网络错误,开发环境使用HTTP。

核心问题原因

  1. 重复请求导致中断:前端同时调用sendPhotosToServer()(Fetch请求)和form.submit()(原生表单提交),触发两次POST请求,浏览器主动中断第一个请求引发NS_binding aborted错误。
  2. 前后端键名不匹配:前端Fetch请求中用photo_${index}作为图片字段名,但后端用request.files.getlist('photo')获取,无法匹配所有图片。
  3. 原生表单未包含全部图片:原生表单提交时,file控件仅保留最后一次选择的文件,uploadedPhotos中的历史图片未被加入表单提交数据。

分步解决方案

1. 移除重复提交,统一用Fetch处理

删除原生表单提交逻辑,仅通过Fetch提交所有数据,避免重复请求:

form.addEventListener(
  "submit",
  async function (event) {
    event.preventDefault();    
    if (!form.checkValidity()) {
      form.classList.add("was-validated");
    } 
    else {
      if (confirm("Are you sure you want to post the ad?")) {       
        const success = await sendPhotosToServer();
        if (success) {
          // 上传成功后跳转首页
          window.location.href = "/";
        }
      }
    }
  },
  false
);

2. 统一FormData字段名,兼容后端获取逻辑

将所有图片统一用photo作为字段名追加到FormData,同时保留原生表单的其他字段:

async function sendPhotosToServer() {
  const formData = new FormData(form); // 先获取表单已有字段

  // 追加所有维护的图片,统一用`photo`作为键名
  uploadedPhotos.forEach((photo) => {
    formData.append("photo", photo);
  });
  
  try {
    const response = await fetch(`/post-ads/${category}/${subcategory}`, {
      method: "POST",
      body: formData,
    });

    if (response.ok) {
      const result = await response.json();
      console.log("Photos uploaded successfully!", result);
      return true;
    } else {
      console.log("Failed to upload photos.", response.status);
      return false;
    }
  } catch (error) {
    console.error("Error during photo upload:", error);
    console.log("An error occurred while uploading photos.");
    return false;
  }
}

3. 修复后端逻辑,正确处理文件并返回JSON

修正后端语法错误,同时注意不能直接将File对象存入数据库,需先保存到服务器再存储路径:

from flask import jsonify
from werkzeug.utils import secure_filename
import os
import json

@postAd.route("/post-ads/<category>/<subcategory>", methods=['GET','POST'])
@login_required
def post_ad(category,subcategory):
    if request.method == 'POST':
        photos = request.files.getlist('photo')
        photo_paths = []
        
        # 保存图片到服务器
        for photo in photos:
            if photo.filename.strip() != '':
                filename = secure_filename(photo.filename)
                # 确保uploads目录存在
                upload_dir = os.path.join(app.root_path, 'uploads')
                os.makedirs(upload_dir, exist_ok=True)
                save_path = os.path.join(upload_dir, filename)
                photo.save(save_path)
                photo_paths.append(filename)
        
        # 将路径列表序列化后存入数据库(需确保Ad模型的photos字段支持字符串存储)
        ad = Ad(photos=json.dumps(photo_paths))
        db.session.add(ad)
        db.session.commit()
        flash('发布广告成功!','success')
        return jsonify({"status": "success", "message": "广告发布完成"})
    
    return render_template("postAd.html", category=category, subcategory=subcategory)

4. 修复删除逻辑的索引匹配问题

原删除逻辑用indexOf可能因为File对象引用问题找不到正确索引,改用唯一ID标识:

function handleImageUpload(e) {
  const files = Array.from(e.target.files);
  files.forEach((file) => {    
    // 生成唯一ID标识文件
    const fileUniqueId = Date.now() + Math.random().toString(36).slice(2);
    file._uniqueId = fileUniqueId;
    uploadedPhotos.push(file);

    const picReader = new FileReader();
    picReader.addEventListener("load", (event) => {
      const imgContainer = document.createElement("div");
      imgContainer.className = "col";
      imgContainer.dataset.fileId = fileUniqueId; // 将ID绑定到DOM元素
      imgContainer.innerHTML = `
        <div class="card">
          <div class="image-container">
            <img src="${event.target.result}" alt="${file.name}" class="card-img-top" style="height: 200px;object-fit: cover;">
          </div>
          <div class="card-body">
            <button class="btn btn-danger btn-sm delete-btn">Delete</button>
          </div>
        </div>
      `;

      const deleteBtn = imgContainer.querySelector(".delete-btn");
      deleteBtn.addEventListener("click", () => {
        imgContainer.remove();
        // 通过唯一ID查找并删除数组中的对应文件
        const index = uploadedPhotos.findIndex(item => item._uniqueId === fileUniqueId);
        if (index > -1) {
          uploadedPhotos.splice(index, 1);
        }
      });
      output.appendChild(imgContainer);      
    });
    picReader.readAsDataURL(file);
  }); 
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.17 06:25:19