凌峰创科服务平台

Android上传图片到PHP服务器,如何实现?

  1. 准备工作
  2. PHP 服务器端代码
  3. Android 客户端代码
  4. 运行与测试
  5. 重要注意事项与最佳实践

准备工作

服务器端:

  • Web 服务器: 你需要一个可以运行 PHP 的环境,最简单的是 XAMPP (包含 Apache 和 PHP)。
  • 目录: 在你的 Web 服务器根目录(XAMPP 的 htdocs)下创建一个文件夹,我们称之为 upload_image
  • 权限: 确保 upload_image 文件夹有写入权限,以便 PHP 可以将文件保存进去,在 Linux/Mac 上,你可能需要执行 chmod 755 upload_imagechmod 777 upload_image(777 权限较大,生产环境需谨慎)。

Android 客户端:

  • 开发环境: Android Studio。
  • 网络权限: 在 AndroidManifest.xml 中必须添加网络权限。
  • 图片选择: 我们将使用 Android 7.0 (API 24) 及以上的官方方式 ActivityResultLauncher 来选择图片。

PHP 服务器端代码

upload_image 文件夹中创建一个名为 upload.php 的文件。

Android上传图片到PHP服务器,如何实现?-图1
(图片来源网络,侵删)

upload.php 代码详解

这个 PHP 脚本负责接收从 Android 发送过来的图片数据,进行验证,然后保存到服务器上。

<?php
// 设置响应头为 JSON,方便 Android 客户端解析
header('Content-Type: application/json');
// --- 1. 配置 ---
// 允许上传的图片类型
$allowedTypes = ['image/jpeg', 'image/png', 'image/gif'];
// 上传目录 (相对于当前脚本位置)
$uploadDir = 'uploads/';
// 最大文件大小 (5MB)
$maxFileSize = 5 * 1024 * 1024;
// --- 2. 创建上传目录 ---
if (!is_dir($uploadDir)) {
    if (!mkdir($uploadDir, 0777, true)) {
        echo json_encode(['success' => false, 'message' => '无法创建上传目录。']);
        exit;
    }
}
// --- 3. 检查是否有文件上传 ---
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
    echo json_encode(['success' => false, 'message' => '没有文件上传或上传出错。']);
    exit;
}
$file = $_FILES['image'];
// --- 4. 验证文件大小 ---
if ($file['size'] > $maxFileSize) {
    echo json_encode(['success' => false, 'message' => '文件过大,最大允许 5MB。']);
    exit;
}
// --- 5. 验证文件类型 (MIME Type) ---
$finfo = new finfo(FILEINFO_MIME_TYPE);
$detectedType = $finfo->file($file['tmp_name']);
if (!in_array($detectedType, $allowedTypes)) {
    echo json_encode(['success' => false, 'message' => '不支持的文件类型,仅支持 JPG, PNG, GIF。']);
    exit;
}
// --- 6. 生成唯一的文件名,防止文件名冲突 ---
// 获取原始文件名和扩展名
$originalName = pathinfo($file['name'], PATHINFO_FILENAME);
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
// 生成新的唯一文件名
$newFileName = uniqid('img_', true) . '.' . $extension;
$destination = $uploadDir . $newFileName;
// --- 7. 移动文件到最终目录 ---
if (move_uploaded_file($file['tmp_name'], $destination)) {
    // 上传成功,返回成功信息和文件路径
    $response = [
        'success' => true,
        'message' => '图片上传成功!',
        'file_path' => $destination // 返回相对路径
    ];
    echo json_encode($response);
} else {
    // 上传失败
    echo json_encode(['success' => false, 'message' => '文件移动失败,请检查目录权限。']);
}
?>

代码解释:

  1. 配置: 定义了允许的图片类型、上传目录和最大文件大小。
  2. 创建目录: 检查 uploads 文件夹是否存在,如果不存在则自动创建。
  3. 检查请求: 通过 $_FILES 超全局变量检查是否有文件被上传。
  4. 验证大小: 检查文件大小是否超过限制。
  5. 验证类型: 使用 finfo 扩展来检测文件的真实 MIME 类型,这比单纯检查文件后缀更安全。
  6. 生成唯一文件名: 使用 uniqid() 生成一个独特的文件名,避免因文件名相同而覆盖已有文件。
  7. 移动文件: move_uploaded_file() 是 PHP 中专门用于处理上传文件的函数,它会把客户端临时文件移动到服务器指定的位置。

Android 客户端代码

这里我们使用现代的 ActivityResultLauncherOkHttp 库来实现。

步骤 3.1: 添加依赖

app/build.gradle 文件的 dependencies 代码块中添加 OkHttp 库。

Android上传图片到PHP服务器,如何实现?-图2
(图片来源网络,侵删)
dependencies {
    // ... 其他依赖
    implementation("com.squareup.okhttp3:okhttp:4.12.0") // 使用最新版本
}

步骤 3.2: 添加网络权限

app/src/main/AndroidManifest.xml 文件中添加以下权限:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<!-- 对于 Android 13 (API 33) 及以上,需要更精确的权限 -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />

步骤 3.3: 编写上传逻辑

在你的 Activity 或 Fragment 中,实现图片选择和上传功能。

import android.app.Activity
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.app.AppCompatActivity
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.MultipartBody
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.asRequestBody
import java.io.File
class MainActivity : AppCompatActivity() {
    private lateinit var selectImageButton: Button
    private lateinit var uploadButton: Button
    private var selectedImageUri: Uri? = null
    // 使用 ActivityResultLauncher 处理图片选择
    private val pickImageLauncher = registerForActivityResult(ActivityResultContracts.GetContent()) { uri: Uri? ->
        uri?.let {
            selectedImageUri = it
            uploadButton.isEnabled = true // 启用上传按钮
            Toast.makeText(this, "图片已选择", Toast.LENGTH_SHORT).show()
        }
    }
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        selectImageButton = findViewById(R.id.btn_select_image)
        uploadButton = findViewById(R.id.btn_upload_image)
        uploadButton.isEnabled = false // 初始禁用上传按钮
        selectImageButton.setOnClickListener {
            // 打开图片选择器
            pickImageLauncher.launch("image/*")
        }
        uploadButton.setOnClickListener {
            selectedImageUri?.let { uri ->
                uploadImage(uri)
            } ?: run {
                Toast.makeText(this, "请先选择一张图片", Toast.LENGTH_SHORT).show()
            }
        }
    }
    private fun uploadImage(imageUri: Uri) {
        // 将 Uri 转换为 File 对象
        val file = File(imageUri.path)
        if (!file.exists()) {
            Toast.makeText(this, "文件不存在", Toast.LENGTH_SHORT).show()
            return
        }
        // 创建 OkHttp 客户端
        val client = OkHttpClient()
        // 创建 MultipartBody.Part,用于上传文件
        val requestBody = file.asRequestBody("image/*".toMediaType())
        val multipartBody = MultipartBody.Part.createFormData(
            "image", // 这里的 key 必须与 PHP 中 $_FILES['image'] 的 'image' 对应
            file.name,
            requestBody
        )
        // 构建 Request
        val request = Request.Builder()
            .url("http://10.0.2.2/upload_image/upload.php") // 使用 10.0.2.2 访问本地服务器
            .post(multipartBody)
            .build()
        // 异步执行请求
        client.newCall(request).enqueue(object : okhttp3.Callback {
            override fun onFailure(call: okhttp3.Call, e: IOException) {
                // 在主线程中更新 UI
                runOnUiThread {
                    Toast.makeText(this@MainActivity, "上传失败: ${e.message}", Toast.LENGTH_LONG).show()
                }
            }
            override fun onResponse(call: okhttp3.Call, response: okhttp3.Response) {
                response.use {
                    if (it.isSuccessful) {
                        val responseBody =
Android上传图片到PHP服务器,如何实现?-图3
(图片来源网络,侵删)
分享:
扫描分享到社交APP
上一篇
下一篇