- 准备工作
- 服务器端 (PHP)
- 创建接收文件的服务器脚本
- 配置服务器 (
.htaccess)
- Android 客户端
- 添加网络权限
- 选择图片 (从相册或相机)
- 将图片转换为
Base64字符串 (最简单的方式) - 使用
OkHttp发送POST请求 - 处理服务器响应
- 完整代码示例
- 重要注意事项与优化
准备工作
在开始之前,你需要:

- 一台 Web 服务器:你可以购买云服务器,或者使用本地环境工具(如 XAMPP, WAMP, MAMP)来模拟服务器。
- PHP 环境:确保你的服务器上安装了 PHP 5.4+ 或更高版本。
- Android Studio:用于开发 Android 应用。
- 网络库:我们推荐使用
OkHttp,它是目前 Android 上最流行的网络请求库。
服务器端 (PHP)
服务器端需要一个脚本来接收客户端上传的文件,我们将使用 Base64 编码的字符串,PHP 需要解码并保存它。
a. 创建 upload.php 文件
在你的网站根目录(htdocs 或 www)下,创建一个名为 uploads 的文件夹,并赋予它可写权限(755 或 777,生产环境请使用更安全的权限)。
创建 upload.php 文件,内容如下:
<?php
// 设置响应头为 JSON,方便客户端解析
header('Content-Type: application/json');
// 定义上传目录,确保这个目录存在并且有写入权限
$uploadDir = 'uploads/';
// 检查上传目录是否存在,如果不存在则创建
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0777, true);
}
// 检查是否有文件通过 POST 请求上传
if (isset($_POST['image'])) {
// 获取 Base64 编码的图片数据
$base64Image = $_POST['image'];
// 移除 Base64 URL 前缀 (data:image/png;base64,)
$ifp = fopen($uploadDir . time() . '.png', 'wb'); // 使用时间戳作为文件名,避免重名
$data = explode(',', $base64Image);
fwrite($ifp, base64_decode($data[1]));
fclose($ifp);
// 返回成功响应
$response = [
'success' => true,
'message' => '图片上传成功!',
'file_path' => $uploadDir . time() . '.png'
];
echo json_encode($response);
} else {
// 如果没有接收到图片数据,返回错误
$response = [
'success' => false,
'message' => '没有接收到图片数据。'
];
echo json_encode($response);
}
?>
代码解释:

header('Content-Type: application/json');:告诉客户端我们将返回 JSON 格式的数据。$uploadDir = 'uploads/';:定义文件保存的目录。isset($_POST['image']):检查 POST 请求中是否存在我们自定义的image字段。explode(',', $base64Image):Base64数据通常以data:image/...;base64,开头,我们需要用逗号分割它,取第二部分(纯编码数据)。base64_decode($data[1]):将纯编码数据解码为二进制文件流。fopen,fwrite,fclose:将解码后的二进制数据写入到服务器文件中。json_encode($response):将 PHP 数组转换为 JSON 字符串并返回给客户端。
b. 配置 .htaccess (可选但推荐)
为了安全,你可以创建一个 .htaccess 文件在你的项目根目录,限制对 upload.php 的直接访问,或者限制上传文件的大小。
# 限制上传文件大小为 10M
php_value upload_max_filesize 10M
php_value post_max_size 10M
# (可选) 禁止列出目录内容
Options -Indexes
Android 客户端
我们将使用 Kotlin 语言来演示,因为它现在是 Android 开发的首选,如果你使用 Java,逻辑基本相同。
a. 添加依赖和权限
在 app/build.gradle 文件中添加 OkHttp 依赖:
dependencies {
// ... 其他依赖
implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
}
在 app/src/main/AndroidManifest.xml 中添加网络权限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- 添加网络权限 -->
<uses-permission android:name="android.permission.INTERNET" />
<!-- 如果需要从相机拍照,还需要相机权限 -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- 如果需要从相册选择,需要读取存储权限 (Android 13+ 是 READ_MEDIA_IMAGES) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 13+ 的媒体图片权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<!-- Android (API 33-) 的存储权限 -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application ...>
...
</application>
</manifest>
重要提示:对于 Android 13 (API 33) 及以上版本,READ_EXTERNAL_STORAGE 已被废弃,需要分别请求 READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_AUDIO,上面的清单文件已包含兼容写法。
b. 选择图片 (从相册)
这里我们使用一个简单的方法,通过 Intent 打开相册。
// 在你的 Activity 或 Fragment 中
private val pickImageRequestCode = 1001
fun openGallery() {
val intent = Intent(Intent.ACTION_PICK).apply {
type = "image/*"
}
startActivityForResult(intent, pickImageRequestCode)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == pickImageRequestCode && resultCode == Activity.RESULT_OK) {
val selectedImageUri: Uri? = data?.data
selectedImageUri?.let {
// 将 URI 转换为 Base64 字符串
val base64String = uriToBase64(it)
// 上传图片
uploadImageToServer(base64String)
}
}
}
// 将 Uri 转换为 Base64 字符串
private fun uriToBaseUri(uri: Uri): String? {
return try {
contentResolver.openInputStream(uri)?.use { inputStream ->
inputStream.readBytes().toBase64()
}
} catch (e: Exception) {
e.printStackTrace()
null
}
}
// 扩展函数,将 ByteArray 转换为 Base64
private fun ByteArray.toBase64(): String {
return android.util.Base64.encodeToString(this, android.util.Base64.NO_WRAP)
}
c. 上传图片到服务器 (使用 OkHttp)
我们来实现上传的核心逻辑。
// 使用 OkHttp 进行网络请求
private fun uploadImageToServer(base64Image: String) {
// 1. 创建 OkHttp 客户端
val client = OkHttpClient()
// 2. 构建 RequestBody
// 我们发送的是表单数据,所以使用 FormBody.Builder
val requestBody = FormBody.Builder()
.add("image", base64Image) // key 必须和 PHP 端 $_POST['image'] 中的 'image' 一致
.build()
// 3. 构建 Request
// !!! 将 YOUR_SERVER_URL 替换为你的实际服务器地址,http://yourdomain.com/upload.php
val request = Request.Builder()
.url("YOUR_SERVER_URL/upload.php") // <--- 修改这里!
.post(requestBody)
.build()
// 4. 异步执行请求
client.newCall(request).enqueue(object : Callback {
override fun onFailure(call: Call, e: IOException) {
// 请求失败,在主线程更新 UI
runOnUiThread {
Toast.makeText(this@YourActivity, "上传失败: ${e.message}", Toast.LENGTH_SHORT).show()
}
}
override fun onResponse(call: Call, response: Response) {
// 请求成功
response.use { resp -> 