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:必须≥8Mpost_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); // 这里可以添加后续的数据库操作等逻辑 } }
最后测试步骤
- 在Unity里运行,查看控制台输出的
Bytes length是否正常(比如几KB到几MB,符合图片大小)。 - 上传后查看Unity控制台的服务器响应,获取真实的错误或成功信息。
- 如果还是失败,根据CI返回的错误信息针对性修复(比如权限问题就调整目录权限,文件大小问题就修改php.ini)。
内容的提问来源于stack exchange,提问作者Dennis Liu

