- Android 端 (Java/Kotlin):使用
OkHttp库来发送网络请求,并处理图片选择和压缩。 - PHP 端:接收上传的图片,进行验证,并保存到服务器指定目录。
- 完整流程:从选择图片到服务器返回成功响应的每一步。
第一步:准备工作 (服务器端)
在开始编码之前,请确保你的服务器环境满足以下条件:

- Web 服务器: 如 Apache 或 Nginx。
- PHP: 版本建议 7.0 或更高。
- 文件上传目录: 在你的网站根目录下创建一个用于存放上传图片的文件夹,
uploads。 - 目录权限: 非常重要! 确保
uploads文件夹具有可写权限,在 Linux 系统上,你可以通过 SSH 执行chmod 755 /path/to/your/uploads来设置。
第二步:服务器端代码 (PHP)
创建一个名为 upload.php 的文件,并将其放在你的网站根目录下。
upload.php
<?php
// 设置响应头为 JSON,方便 Android 客户端解析
header('Content-Type: application/json');
// --- 配置 ---
$targetDir = "uploads/"; // 上传文件保存的目录
$allowTypes = array('jpg', 'jpeg', 'png', 'gif'); // 允许的文件类型
// --- 1. 检查请求方法是否为 POST ---
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
// --- 2. 检查是否有文件上传 ---
if (!isset($_FILES['image'])) {
http_response_code(400); // Bad Request
echo json_encode(['status' => 'error', 'message' => 'No file uploaded.']);
exit;
}
$file = $_FILES['image'];
$fileName = basename($file["name"]);
$targetFilePath = $targetDir . $fileName;
$fileType = pathinfo($targetFilePath, PATHINFO_EXTENSION);
// --- 3. 验证文件类型 ---
if (!in_array(strtolower($fileType), $allowTypes)) {
http_response_code(415); // Unsupported Media Type
echo json_encode(['status' => 'error', 'message' => 'Sorry, only JPG, JPEG, PNG & GIF files are allowed.']);
exit;
}
// --- 4. 验证文件大小 (限制为 5MB) ---
$maxFileSize = 5 * 1024 * 1024; // 5MB
if ($file["size"] > $maxFileSize) {
http_response_code(413); // Payload Too Large
echo json_encode(['status' => 'error', 'message' => 'Sorry, your file is too large. Max size is 5MB.']);
exit;
}
// --- 5. 移动上传的文件到目标目录 ---
if (move_uploaded_file($file["tmp_name"], $targetFilePath)) {
// 上传成功
$response = [
'status' => 'success',
'message' => 'File uploaded successfully.',
'file_path' => $targetFilePath, // 返回文件在服务器上的相对路径
'file_url' => 'http://yourdomain.com/' . $targetFilePath // 返回文件的完整访问URL
];
echo json_encode($response);
} else {
// 上传失败
http_response_code(500); // Internal Server Error
echo json_encode(['status' => 'error', 'message' => 'Sorry, there was an error uploading your file.']);
}
} else {
// 如果不是 POST 请求
http_response_code(405); // Method Not Allowed
echo json_encode(['status' => 'error', 'message' => 'Invalid request method.']);
}
?>
请务必将 http://yourdomain.com/ 替换为你自己的域名。
第三步:Android 端代码 (Java)
我们将使用 OkHttp 库,因为它非常强大且易于使用,为了优化上传体验和节省流量,我们会先对图片进行压缩。
添加依赖
在你的 app/build.gradle 文件的 dependencies 代码块中添加 OkHttp 和图片压缩库 Glide (Glide 可以方便地获取和压缩 Bitmap)。

dependencies {
// ... 其他依赖
implementation 'com.squareup.okhttp3:okhttp:4.9.3'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
}
添加网络权限
在 app/manifests/AndroidManifest.xml 中添加网络权限。
<manifest ...>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- Android 11 (API 30)及以上需要此权限来访问所有文件 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" /> <!-- Android 13+ -->
<application ...>
...
</application>
</manifest>
创建上传工具类
创建一个 UploadUtil.java 类来封装上传逻辑。
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Log;
import androidx.annotation.NonNull;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.TimeUnit;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
import okhttp3.Response;
public class UploadUtil {
private static final String TAG = "UploadUtil";
private static final String UPLOAD_URL = "http://yourdomain.com/upload.php"; // 替换为你的PHP文件地址
private static final MediaType MEDIA_TYPE_PNG = MediaType.parse("image/png");
private static OkHttpClient client;
// 初始化 OkHttpClient
private static OkHttpClient getOkHttpClient() {
if (client == null) {
client = new OkHttpClient.Builder()
.connectTimeout(15, TimeUnit.SECONDS)
.readTimeout(15, TimeUnit.SECONDS)
.writeTimeout(20, TimeUnit.SECONDS)
.build();
}
return client;
}
/**
* 上传图片到服务器
* @param context 上下文
* @param imagePath 图片的本地路径
* @param callback 上传结果回调
*/
public static void uploadImage(Context context, String imagePath, final UploadCallback callback) {
// 1. 检查文件是否存在
File imageFile = new File(imagePath);
if (!imageFile.exists()) {
callback.onFailure("Image file not found");
return;
}
// 2. 压缩图片 (可选但推荐)
Bitmap compressedBitmap = compressImage(imageFile, 1024, 1024); // 压缩到 1024x1024 以内
File compressedFile = new File(context.getCacheDir(), "compressed_" + imageFile.getName());
try {
// 将压缩后的Bitmap保存到临时文件
java.io.FileOutputStream out = new java.io.FileOutputStream(compressedFile);
compressedBitmap.compress(Bitmap.CompressFormat.JPEG, 80, out);
out.flush();
out.close();
} catch (IOException e) {
callback.onFailure("Failed to compress image: " + e.getMessage());
return;
}
// 3. 创建请求体
RequestBody requestBody = new MultipartBody.Builder()
.setType(MultipartBody.FORM)
.addFormDataPart("image", compressedFile.getName(),
RequestBody.create(MEDIA_TYPE_PNG, compressedFile))
.build();
// 4. 创建请求
Request request = new Request.Builder()
.url(UPLOAD_URL)
.post(requestBody)
.build();
// 5. 异步执行请求
getOkHttpClient().newCall(request).enqueue(new okhttp3.Callback() {
@Override
public void onFailure(@NonNull okhttp3.Call call, @NonNull IOException e) {
// 网络错误或请求失败
callback.onFailure("Upload failed: " + e.getMessage());
}
@Override
public void onResponse(@NonNull okhttp3.Call call, @NonNull Response response) throws IOException {
// 服务器返回响应
if (response.isSuccessful()) {
String responseBody = response.body().string();
Log.d(TAG, "Server Response: " + responseBody);
callback.onSuccess(responseBody);
} else {
// 服务器返回错误状态码
callback.onFailure("Server error: " + response.code());
}
}
});
}
/**
* 简单的图片压缩方法
*/
private static Bitmap compressImage(File file, int reqWidth, int reqHeight) {
final BitmapFactory
