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

Unity结合Codeigniter上传图片失败问题排查与解决求助

排查Unity到CodeIgniter图片上传失败的问题

看起来你遇到的核心问题是:用Unity Native Camera拍摄的JPG图片上传到CodeIgniter服务器时,$this->upload->do_upload("ocr_image")始终返回失败,但你看不到真实的错误原因。我来帮你一步步排查并修复这个问题。

第一步:先获取CodeIgniter上传的真实错误信息

你现在在if(!$this->upload->do_upload("ocr_image"))分支里硬编码了错误提示,但这个提示和你的配置完全不符(你的config里max_size是8096KB=8MB,而提示写的是2MB),这会严重误导排查方向。首先要替换成CI上传类的真实错误信息,这样才能知道到底哪里出了问题:

if(!$this->upload->do_upload("ocr_image")) {
    // 输出真实的上传错误,替换硬编码内容
    echo $this->upload->display_errors();
    exit;
} else {
    // 后续逻辑...
}

这一步是关键,运行后你就能看到具体的失败原因——比如权限不足、文件类型不匹配、文件名重复、文件大小超出php.ini限制等等。

常见失败原因及修复方案

1. 服务器目录权限与存在性问题

CodeIgniter的上传目录./assets/ocr_image/需要满足两个条件:

  • 目录实际存在:CI默认不会自动创建上传目录,所以先手动创建这个目录,或者在代码里添加自动创建逻辑(后面的完整代码会包含)。
  • 目录有写入权限:给目录设置755权限(服务器环境下足够安全),如果还是不行再尝试777(不推荐长期使用,仅用于测试)。

2. PHP配置的文件大小限制

即使你在CI的config里设置了max_size = 8096,还要检查服务器的php.ini配置:

  • upload_max_filesize:必须≥8M
  • post_max_size:必须大于upload_max_filesize(比如设置成10M)
  • max_execution_time:如果上传大文件,需要适当延长执行时间(比如设置成30)

3. Unity端的图片数据是否正确

你的Unity代码里,读取图片字节的方式是byte[] bytes = File.ReadAllBytes(path);,这里有几个潜在问题:

  • 确认path是有效的图片路径:从Debug.Log("Image path: " + path)输出的路径,检查设备上是否存在该文件,文件大小是否正常。
  • 避免文件占用:NativeCamera加载texture后,虽然你调用了Destroy(texture),但File.ReadAllBytes是同步读取,可能存在文件占用风险。建议直接用NativeCamera的GetImageRawData()方法获取字节数组:
    // 替换File.ReadAllBytes(path)为:
    byte[] bytes = NativeCamera.GetImageRawData(path);
    Debug.Log("Bytes length: " + bytes.Length); // 确认字节数合理(比如几KB到几MB)
    
  • UnityWebRequest的写法更新:Unity 2020+版本中Send()已被弃用,推荐用SendWebRequest(),同时用www.result判断请求结果,比isNetworkError更全面。

4. CodeIgniter的文件类型验证问题

你用exif_imagetype判断图片类型,但有些图片可能没有EXIF信息,导致exif_imagetype返回false,直接触发类型错误。可以移除这个手动判断,依赖CI上传类自带的类型验证即可——它会更可靠。

另外,$config['max_size']建议用数字而不是字符串,避免CI解析错误。

5. 文件名重复问题

你的$config['file_name'] = $phone_code.$phone_number;如果同一个用户多次上传,文件名会重复,而$config['overwrite'] = FALSE会导致上传失败。可以给文件名加上时间戳,确保唯一性:

$config['file_name'] = $phone_code.$phone_number."_".time();

修正后的完整代码示例

Unity C#代码

public void TakePicture(int maxSize) {
    if (NativeCamera.IsCameraBusy()) {
        Debug.Log("Camera Busy");
        return;
    }

    NativeCamera.Permission permission = NativeCamera.TakePicture((path) => {
        Debug.Log("Image path: " + path);
        if (path == null) {
            Debug.Log("No image captured");
            return;
        }

        // 直接获取图片原始字节数据,避免文件读取的潜在问题
        byte[] bytes = NativeCamera.GetImageRawData(path);
        if(bytes == null || bytes.Length == 0) {
            Debug.Log("Failed to get image raw data");
            return;
        }
        Debug.Log("Bytes length: " + bytes.Length);

        StartCoroutine(upload_ocr_image(bytes));

        // 预览图片的逻辑保留
        Texture2D texture = NativeCamera.LoadImageAtPath(path, maxSize);
        if (texture == null) {
            Debug.Log("Couldn't load texture from " + path);
            return;
        }

        GameObject quad = GameObject.CreatePrimitive(PrimitiveType.Quad);
        quad.transform.position = Camera.main.transform.position + Camera.main.transform.forward * 2.5f;
        quad.transform.forward = Camera.main.transform.forward;
        quad.transform.localScale = new Vector3(1f, texture.height / (float)texture.width, 1f);
        Material material = quad.GetComponent<Renderer>().material;
        if (!material.shader.isSupported)
            material.shader = Shader.Find("Legacy Shaders/Diffuse");
        material.mainTexture = texture;
        Destroy(quad, 5f);
        Destroy(texture, 5f);
    }, maxSize);

    Debug.Log("Permission result: " + permission);
}

IEnumerator upload_ocr_image(byte[] bytes) {
    WWWForm formData = new WWWForm();
    formData.AddField("frameCount", Time.frameCount.ToString());
    // 指定正确的MIME类型,确保服务器识别
    formData.AddBinaryData("ocr_image", bytes, "ocr.jpg", "image/jpeg");
    formData.AddField("phone_code", "+61");
    formData.AddField("phone_number", "434599859");

    using (UnityWebRequest www = UnityWebRequest.Post(web_url.url_upload_ocr_image, formData)) {
        yield return www.SendWebRequest(); // Unity 2020+推荐用法

        if (www.result != UnityWebRequest.Result.Success) {
            Debug.Log("Upload error: " + www.error);
        } else {
            Debug.Log("Server response: " + www.downloadHandler.text);
        }
    }
}

CodeIgniter PHP代码

function upload_ocr_image() {
    // 先检查必要参数是否存在
    $phone_code = $this->input->post('phone_code', true);
    $phone_number = $this->input->post('phone_number', true);
    if(empty($phone_code) || empty($phone_number)) {
        echo "Missing phone code or number";
        exit;
    }

    $upload_dir = './assets/ocr_image/';
    // 自动创建上传目录(如果不存在)
    if(!is_dir($upload_dir)) {
        mkdir($upload_dir, 0755, true); // 递归创建目录
    }

    $config['upload_path'] = $upload_dir;
    $config['allowed_types'] = 'gif|jpg|png|jpeg';
    // 生成唯一文件名,避免重复
    $config['file_name'] = $phone_code.$phone_number."_".time();
    $config['overwrite'] = FALSE;
    $config['max_size'] = 8096; // 单位:KB,即8MB
    $config['max_width'] = 6000;
    $config['max_height'] = 6000;

    $this->load->library('upload', $config);

    if(!$this->upload->do_upload("ocr_image")) {
        // 输出真实的上传错误
        echo "Upload failed: " . $this->upload->display_errors();
        exit;
    } else {
        $img_data = $this->upload->data();
        echo "Upload successful! File details: " . json_encode($img_data);
        // 这里可以添加后续的数据库操作等逻辑
    }
}

最后测试步骤

  1. 在Unity里运行,查看控制台输出的Bytes length是否正常(比如几KB到几MB,符合图片大小)。
  2. 上传后查看Unity控制台的服务器响应,获取真实的错误或成功信息。
  3. 如果还是失败,根据CI返回的错误信息针对性修复(比如权限问题就调整目录权限,文件大小问题就修改php.ini)。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.12 04:55:43