使用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
相关产品推荐
相关产品推荐

