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

使用Retrofit2上传手机拍摄图片至PHP服务器出现空白文件问题

Retrofit2上传图片到PHP服务器为空问题解决

核心问题分析

  • Android 10+的Scoped Storage限制,MediaStore.Images.Media.DATA字段已废弃,直接通过该字段获取的路径可能无法访问到实际文件,导致上传空文件。
  • 直接使用Content URI对应的File对象可能不存在或无法读取,导致RequestBody为空。

Android端代码修正

1. 替换文件获取逻辑,改用临时文件上传

不再通过getPathFromUri获取路径,而是通过ContentResolver将URI对应的内容复制到临时文件,再上传这个临时文件:

private fun enviarImage(imageUri: Uri) {
    val context = requireContext()
    val tempFile = try {
        // 创建临时文件
        val timeStamp = SimpleDateFormat("yyyyMMdd_HHmmss", Locale.getDefault()).format(Date())
        val tempFile = File(context.cacheDir, "temp_$timeStamp.jpg")
        // 从URI复制内容到临时文件
        context.contentResolver.openInputStream(imageUri)?.use { inputStream ->
            tempFile.outputStream().use { outputStream ->
                inputStream.copyTo(outputStream)
            }
        }
        tempFile
    } catch (e: IOException) {
        e.printStackTrace()
        Toast.makeText(context, "无法读取图片文件", Toast.LENGTH_SHORT).show()
        return
    }

    val requestBody = tempFile.asRequestBody("image/jpeg".toMediaType())
    val imagePart = MultipartBody.Part.createFormData("img", tempFile.name, requestBody)

    val id = Global_Class.codigoFin
    val cliente = Global_Class.codigo_cliente

    // 确保字符串参数正确包装为RequestBody
    val idRequestBody = RequestBody.create("text/plain".toMediaType(), id)
    val clienteRequestBody = RequestBody.create("text/plain".toMediaType(), cliente)

    GlobalScope.launch(Dispatchers.IO) {
        try {
            val response = webService.enviarImage(idRequestBody, clienteRequestBody, imagePart)
            requireActivity().runOnUiThread {
                if (response.isSuccessful) {
                    Toast.makeText(context, "图片上传成功", Toast.LENGTH_SHORT).show()
                } else {
                    Toast.makeText(context, "上传失败:${response.code()}", Toast.LENGTH_SHORT).show()
                }
            }
            // 删除临时文件
            tempFile.delete()
        } catch (e: Exception) {
            e.printStackTrace()
            requireActivity().runOnUiThread {
                Toast.makeText(context, "上传出错", Toast.LENGTH_SHORT).show()
            }
            tempFile.delete()
        }
    }
}

2. 更新WebService接口

将字符串参数改为RequestBody类型,确保Multipart请求中参数正确传递:

interface WebService {
    @Multipart
    @POST("upload.php")
    suspend fun enviarImage(
        @Part("id") id: RequestBody,
        @Part("cliente") cliente: RequestBody,
        @Part img: MultipartBody.Part
    ): Response<JsonObject>
}

3. 移除无效方法

直接删除getPathFromUri函数,因为不再使用该方式获取文件路径。


PHP端检查与修正

1. 确保uploads目录权限

在服务器上创建uploads目录并设置权限:

mkdir uploads
chmod 755 uploads

2. 增强错误日志输出

修改PHP代码,添加详细错误信息方便调试:

<?php
error_reporting(E_ALL);
ini_set('display_errors', 1);

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    // 检查POST参数
    $id = isset($_POST['id']) ? $_POST['id'] : '未获取到id';
    $cliente = isset($_POST['cliente']) ? $_POST['cliente'] : '未获取到cliente';
    
    $uploadsDir = 'uploads/';
    
    // 确保目录存在
    if (!file_exists($uploadsDir)) {
        mkdir($uploadsDir, 0755, true);
    }
    
    // 打印文件信息用于调试
    error_log("Received files: " . print_r($_FILES, true));
    
    if (isset($_FILES['img'])) {
        $error = $_FILES['img']['error'];
        error_log("File error code: $error");
        
        if ($error === UPLOAD_ERR_OK) {
            $tmpName = $_FILES['img']['tmp_name'];
            $fileName = $_FILES['img']['name'];
            $destination = $uploadsDir . $fileName;
            
            if (move_uploaded_file($tmpName, $destination)) {
                echo json_encode(['message' => '图片接收成功', 'id' => $id, 'cliente' => $cliente]);
            } else {
                echo json_encode(['error' => '无法移动文件到目标目录', 'destination' => $destination]);
            }
        } else {
            $errorMsg = match($error) {
                UPLOAD_ERR_INI_SIZE => '文件超过php.ini限制',
                UPLOAD_ERR_FORM_SIZE => '文件超过表单限制',
                UPLOAD_ERR_PARTIAL => '文件仅部分上传',
                UPLOAD_ERR_NO_FILE => '未选择文件',
                UPLOAD_ERR_NO_TMP_DIR => '服务器缺少临时目录',
                UPLOAD_ERR_CANT_WRITE => '无法写入磁盘',
                UPLOAD_ERR_EXTENSION => '文件上传被扩展中断',
                default => '未知错误'
            };
            echo json_encode(['error' => $errorMsg]);
        }
    } else {
        echo json_encode(['error' => '未接收到图片文件']);
    }
} else {
    echo json_encode(['error' => '不允许的请求方法']);
}
?>

额外注意事项

  • 确保AndroidManifest.xml中添加网络权限:<uses-permission android:name="android.permission.INTERNET"/>
  • Android 13+需添加图片读写权限:<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
  • 避免使用GlobalScope,建议使用ViewModel的CoroutineScope防止内存泄漏。

内容的提问来源于stack exchange,提问作者Abraham Santillan Silva

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 11:22:32