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

使用Retrofit向Slim框架API上传图片遇500错误求助

问题分析

你在把图片上传逻辑迁移到Slim框架时踩了几个典型的小坑,Call to a member function getError() on null这个错误的核心原因有两个:

  • 后端代码没有正确从$request->getUploadedFiles()的结果中获取具体的上传文件对象,甚至缺失了关键的目录定义和文件处理函数
  • Android端的Retrofit请求定义和Slim后端的路由格式不匹配

解决方案

1. 修复Slim后端代码

先把后端的错误点修正,同时适配多文件上传的需求:

<?php
use Slim\Http\Request;
use Slim\Http\Response;

$app->post('/uploadprofilepicture/{userid}', function (Request $request, Response $response) {
    $db = new Functions();
    $userFk = $request->getAttribute('userid');
    $result['error'] = true;
    $result['message'] = 'Initial state';
    
    // 1. 定义上传目录(确保这个目录存在且有写入权限)
    $directory = __DIR__ . '/../images/iduploads/';
    if (!is_dir($directory)) {
        mkdir($directory, 0777, true);
    }

    try {
        // 2. 获取所有上传的文件
        $uploadedFiles = $request->getUploadedFiles();
        
        // 3. 检查是否有文件上传(对应Android端的参数名"file[]")
        if (!empty($uploadedFiles['file[]'])) {
            $uploadedFileList = $uploadedFiles['file[]'];
            $fileNames = [];
            
            // 4. 遍历处理每个上传的文件
            foreach ($uploadedFileList as $uploadedFile) {
                if ($uploadedFile->getError() === UPLOAD_ERR_OK) {
                    // 调用文件移动函数
                    $filename = moveUploadedFile($directory, $uploadedFile);
                    $fileNames[] = $filename;
                    $result['error'] = false;
                    $result['message'] = 'Upload success for image: ' . $filename;
                } else {
                    throw new Exception('File upload error code: ' . $uploadedFile->getError());
                }
            }
            
            // 5. 如果有成功上传的文件,调用数据库存储方法
            if (!empty($fileNames)) {
                // 适配你之前的数据库方法参数(最多3个文件,不足的传null)
                $db->uploadIdentificationDocs(
                    $userFk,
                    $fileNames[0] ?? null,
                    $fileNames[1] ?? null,
                    $fileNames[2] ?? null
                );
            }
        } else {
            throw new Exception('No files were uploaded');
        }
    } catch (Exception $e) {
        $result['error'] = true;
        $result['message'] = 'Error occurred: ' . $e->getMessage();
    }

    return $response->withJson($result);
});

// 6. 实现Slim推荐的文件移动辅助函数
function moveUploadedFile($directory, $uploadedFile) {
    // 沿用你之前的唯一文件名生成逻辑
    function microtime_float(){
        list($usec, $sec) = explode(" ", microtime());
        return ((float)$usec + (float)$sec);
    }
    $extension = pathinfo($uploadedFile->getClientFilename(), PATHINFO_EXTENSION);
    $filename = microtime_float() . '_' . rand(1, 99999999) . '.' . $extension;
    
    $uploadedFile->moveTo($directory . DIRECTORY_SEPARATOR . $filename);
    return $filename;
}
?>

关键修复点:

  • 定义了上传目录并增加了自动创建逻辑
  • 正确从$uploadedFiles数组中获取名为file[]的文件列表(和Android端参数名对应)
  • 实现了Slim规范的moveUploadedFile辅助函数
  • 用Slim自带的withJson方法返回响应,更符合框架规范
  • 增加了错误细节捕获,便于调试

2. 调整Android端Retrofit请求代码

你的Retrofit接口定义和Slim路由不匹配,需要修改成@Multipart注解,同时把userid作为路径参数传递:

第一步:修正Retrofit接口

public interface APIService {
    @Multipart
    @POST("uploadprofilepicture/{userid}")
    Call<Result> uploadMultiFile(
        @Path("userid") String userId,
        @Part List<MultipartBody.Part> files
    );
}

第二步:调整请求构建代码

APIService service = retrofit.create(APIService.class);

// 构建多文件列表
List<MultipartBody.Part> fileParts = new ArrayList<>();

// 示例:添加第一个文件(可重复添加多个)
if (profilePicture != null) {
    RequestBody fileRequestBody = RequestBody.create(MediaType.parse("image/*"), profilePicture);
    // 参数名保持"file[]"和后端对应
    MultipartBody.Part filePart = MultipartBody.Part.createFormData("file[]", profilePicture.getName(), fileRequestBody);
    fileParts.add(filePart);
}

User user = SharedPreferencesUtils.getUserPreferences(this);
// 发起请求,userid作为路径参数传递
Call<Result> call = service.uploadMultiFile(user.getUserId(), fileParts);

// 如需进度监听,把fileRequestBody替换为ProgressRequestBody即可
// RequestBody progressBody = new ProgressRequestBody(fileRequestBody, this);
// MultipartBody.Part filePart = MultipartBody.Part.createFormData("file[]", profilePicture.getName(), progressBody);

关键调整点:

  • 用@Multipart标记接口,这是Retrofit处理表单上传的标准方式
  • 把userid从FormData参数改为路径参数,匹配Slim的路由定义
  • 用List<MultipartBody.Part>传递多文件,参数名和后端保持一致

3. 调试验证建议

  • 先测后端:用Postman发送POST请求到/uploadprofilepicture/你的用户ID,选择form-data,添加file[]参数并上传文件,确认后端返回正确的JSON响应
  • 检查权限:确保Slim项目的../images/iduploads/目录有写入权限(Linux下可临时用chmod -R 777 目录路径测试)
  • 查看日志:如果还是500错误,打开Slim的logs/目录下的错误日志,里面有详细的堆栈信息,能快速定位问题

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 04:20:48