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

KaiOS设备图片上传遇网络错误:FormData与Base64方案排查

KaiOS设备图片上传问题解决方案

问题描述

在KaiOS设备开发应用的图片上传功能时,尝试Base64和FormData两种方式均失败,报错:

Network error when attempting to fetch resources

仅纯文本POST请求可正常工作,两种方案在桌面/移动浏览器中均无问题,仅KaiOS设备出现异常。

尝试过的代码

1. Base64上传方式

const handleCreatePost = async () => {
    // Convert all images to base64
    const base64Images = await Promise.all(
        postData?.files?.map((file) => blobToBase64(file))
    );

    createPost(
        {
            content: postData.postContent,
            files: base64Images.map((img, i) => ({
                originalname: postData.files[i].name,
                mimetype: postData.files[i].type,
                data: img,
                size: postData.files[i].size,
            })),
        },
        {
            onSuccess: () => {
                toast.success("Post created successfully");
                router.push(ALL_LINKS.EXPLORE_FEED.pageLink);
            },
            onError: () => {
                toast.error("Failed to create post");
            },
        }
    );
};

export function blobToBase64(blob: Blob): Promise<string> {
    return new Promise((resolve, reject) => {
        const reader = new FileReader();
        reader.onloadend = () => {
            const result = reader.result as string;
            // Remove the "data:*/*;base64," prefix if present
            const base64 = result.includes(",") ? result.split(",")[1] : result;
            resolve(base64);
        };
        reader.onerror = (error) => reject(error);
        reader.readAsDataURL(blob);
    });
}

2. FormData上传方式

const handleSubmit = async (values: any) => {
    console.log(values);
    mutate(
        {
            username: values.username,
            bio: values.bio,
            profilePicture: values.profilePicture,
        },
        {
            onSuccess: () => {
                router.push(
                    `${ALL_LINKS.EXPLORE_PROFILE_SETTINGS.pageLink}?name=${values.username}`
                );
            },
        }
    );
};

export const basicInfo = async (payload: basicInfoPayload): Promise<BasicInfoResponse> => {
    try {
        // Create FormData using utility function
        const formData = objectToFormData(payload, {
            arrayFormat: "indexed", // exploreTags will be exploreTags[0], exploreTags[1], etc.
            excludeNull: true,
            excludeUndefined: true,
        });

        const res = await http.post(API_ROUTES.AUTH.basicInfo.url, formData, {
            "Content-Type": "multipart/form-data",
        });
        return res as BasicInfoResponse;
    } catch (error) {
        console.error("Unable to add information:", error);
        throw error;
    }
};

可行解决方案

1. 修复FormData请求的Content-Type设置

KaiOS的fetch实现对手动设置multipart/form-data的边界值支持不佳,不要手动指定Content-Type,让浏览器自动生成包含正确边界的请求头:

export const basicInfo = async (payload: basicInfoPayload): Promise<BasicInfoResponse> => {
    try {
        const formData = objectToFormData(payload, {
            arrayFormat: "indexed",
            excludeNull: true,
            excludeUndefined: true,
        });

        // 移除手动设置的Content-Type,让浏览器自动处理
        const res = await http.post(API_ROUTES.AUTH.basicInfo.url, formData);
        return res as BasicInfoResponse;
    } catch (error) {
        console.error("Unable to add information:", error);
        throw error;
    }
};

2. 优化Base64请求的 payload 大小与格式

KaiOS对大体积Base64数据的HTTP请求兼容性差,建议:

  • 限制单张图片分辨率(如压缩至640x480),减少Base64字符串长度
  • 确保请求头设置为Content-Type: application/json,避免解析错误
  • 分批次上传多张图片,避免单次请求体积过大

示例压缩图片的工具函数:

export async function compressBlob(blob: Blob, maxWidth = 640, maxHeight = 480): Promise<Blob> {
    return new Promise((resolve) => {
        const img = new Image();
        img.onload = () => {
            const canvas = document.createElement('canvas');
            let width = img.width;
            let height = img.height;

            if (width > height && width > maxWidth) {
                height *= maxWidth / width;
                width = maxWidth;
            } else if (height > maxWidth) {
                width *= maxHeight / height;
                height = maxHeight;
            }

            canvas.width = width;
            canvas.height = height;
            const ctx = canvas.getContext('2d');
            ctx?.drawImage(img, 0, 0, width, height);
            canvas.toBlob(resolve, blob.type, 0.7);
        };
        img.src = URL.createObjectURL(blob);
    });
}

使用时先压缩再转Base64:

const base64Images = await Promise.all(
    postData?.files?.map(async (file) => {
        const compressedBlob = await compressBlob(file);
        return blobToBase64(compressedBlob);
    })
);

3. 检查KaiOS的网络权限与CORS配置

  • 确保应用在manifest.webapp中声明了网络权限:
    "permissions": {
        "network": {}
    }
    
  • 后端需正确配置CORS,允许KaiOS设备的请求来源,避免因跨域导致的网络错误

4. 改用XMLHttpRequest替代fetch

KaiOS的fetch实现存在部分兼容性bug,切换到XMLHttpRequest可能解决问题:

export function uploadFormData(formData: FormData, url: string): Promise<any> {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('POST', url);
        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(JSON.parse(xhr.responseText));
            } else {
                reject(new Error(`Request failed with status ${xhr.status}`));
            }
        };
        xhr.onerror = () => reject(new Error('Network error occurred'));
        xhr.send(formData);
    });
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 05:40:02