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

Laravel转纯PHP:Flutter多图上传至PHP数据库技术问询

需求说明

我原本参考Laravel代码做开发,现在要改成纯PHP代码。前端用Flutter框架,已经靠image picker包实现了多图选择上传的功能,后端用PHP,需要完成把图片信息存入数据库的逻辑。

更新:前端多图上传的代码已经写好,如下:

class AddFlatGallery extends Stateful Widget {
  const AddFlatGallery({Key? key}) : super(key: key);

  @override
  State<AddFlatGallery> createState() => _AddFlatGalleryState();
}

class _AddFlatGalleryState extends State<AddFlatGallery> {
  String flatname = '';
  String buildingname = '';
  String flatlocation = '';

  Future CheckFlat() async {
    SharedPreferences preferencesaddflat = await SharedPreferences.getInstance();
    setState(() {
      flatname = preferencesaddflat.getString('flatname') ?? "Not found";
      buildingname = preferencesaddflat.getString('buildingname') ?? "Not found";
      flatlocation = preferencesaddflat.getString('flatlocation') ?? "Not found";
    });
  }

  late List<Asset> images = [];
  Dio dio = Dio();

  Widget buildGridView() {
    return GridView.count(
        crossAxisCount: 3,
        crossAxisSpacing: 7,
        children: List.generate(images.length, (index) {
          Asset asset = images[index];
          return AssetThumb(
            asset: asset,
            width: 300,
            height: 300,
          );
        }));
  }

  Future<void> loadAssets() async {
    List<Asset> resultList = [];

    try {
      resultList = await MultiImagePicker.pickImages(
          maxImages: 300,
          enableCamera: true,
          selectedAssets: images,
          cupertinoOptions: const CupertinoOptions(takePhotoIcon: "chat"),
          materialOptions: const MaterialOptions(
            actionBarColor: "#abcdef",
            actionBarTitle: "TenantPay",
            allViewTitle: "All Photos",
            useDetailsView: false,
            selectCircleStrokeColor: "#000000",
          ));
    } on Exception catch (e) {
      print(e.toString());
    }
    if (!mounted) return;

    if (mounted) {
      setState(() {
        images = resultList;
      });
    }
  }

  final Timer _timer = Timer.periodic(const Duration(seconds: 5), ((timer) {}));

  @override
  void dispose() {
    super.dispose();
    _timer.cancel(); //cancel the timer here
  }

  _saveImage() async {
    if (images.isNotEmpty) {
      for (var i = 0; i < images.length; i++) {
        ByteData byteData = await images[i].getByteData();
        List<int> imageData = byteData.buffer.asUint8List();

        MultipartFile multipartFile = MultipartFile.fromBytes(imageData,
            filename: images[i].name, contentType: MediaType('image', 'jpg'));

        FormData formData = FormData.fromMap({
          "image": multipartFile,
          "flat": flatname,
          "building": buildingname,
          "location": flatlocation,
        });

        var response = await dio.post(ApiConstant.flatuploadurl, data: formData);

        if (response.statusCode == 200) {
          print(response.data);

          Fluttertoast.showToast(
              msg: "Images are added successfully",
              toastLength: Toast.LENGTH_SHORT,
              gravity: ToastGravity.CENTER,
              timeInSecForIosWeb: 3,
              backgroundColor: Colors.green,
              textColor: Colors.white,
              fontSize: 16.0);
          Navigator.of(context).pop();
          Navigator.push(
            context,
            MaterialPageRoute(
              builder: (context) => const Home(),
            ),
          );
        }
      }
    }
  }

  @override
  void initState() {
    super.initState();
    CheckFlat();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        backgroundColor: const Color.fromARGB(255, 30, 64, 122),
        title: const Text(
          'Upload Images',
        ),
      ),
      body: Padding(
        padding: const EdgeInsets.all(20.0),
        child: Column(
          children: [
            FloatingActionButton(
              onPressed: () {
                loadAssets();
              },
              child: const Icon(Icons.add),
            ),
            Expanded(child: buildGridView()),
            Container(
              height: 60,
              padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
              child: CustomButton(
                text: 'Save',
                color: Colors.orange,
                onTap: () {
                  _saveImage();
                },
              ),
            ),
            Container(
              height: 60,
              padding: const EdgeInsets.fromLTRB(10, 10, 10, 0),
              child: CustomButton(
                text: 'Continue without adding Images',
                color: Colors.blueAccent,
                onTap: () {
                  Navigator.push(
                    context,
                    MaterialPageRoute(
                      builder: (context) => const Home(),
                    ),
                  );
                },
              ),
            ),
          ],
        ),
      ),
    );
  }
}
纯PHP后端实现

1. 数据库表准备

先创建存储图片信息的表,执行以下SQL:

CREATE TABLE flat_gallery (
    id INT AUTO_INCREMENT PRIMARY KEY,
    flat_name VARCHAR(255) NOT NULL,
    building_name VARCHAR(255) NOT NULL,
    location VARCHAR(255) NOT NULL,
    image_path VARCHAR(255) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

2. 上传处理代码(upload_flat_images.php)

<?php
// 数据库连接配置,根据自己的实际情况修改
$servername = "localhost";
$username = "your_db_username";
$password = "your_db_password";
$dbname = "your_db_name";

// 创建数据库连接
$conn = new mysqli($servername, $username, $password, $dbname);
if ($conn->connect_error) {
    die(json_encode(["status" => "error", "message" => "数据库连接失败: " . $conn->connect_error]));
}

// 允许的图片类型
$allowed_types = ['image/jpeg', 'image/jpg'];
// 最大文件大小(5MB)
$max_size = 5 * 1024 * 1024;

// 检查是否有文件上传
if (!isset($_FILES['image']) || $_FILES['image']['error'] !== UPLOAD_ERR_OK) {
    echo json_encode(["status" => "error", "message" => "图片上传失败"]);
    exit;
}

// 接收表单参数
$flat_name = $_POST['flat'] ?? '';
$building_name = $_POST['building'] ?? '';
$location = $_POST['location'] ?? '';

// 验证参数是否完整
if (empty($flat_name) || empty($building_name) || empty($location)) {
    echo json_encode(["status" => "error", "message" => "参数不完整"]);
    exit;
}

$file = $_FILES['image'];
$file_type = $file['type'];
$file_size = $file['size'];
$file_name = basename($file['name']);

// 验证文件类型
if (!in_array($file_type, $allowed_types)) {
    echo json_encode(["status" => "error", "message" => "只允许上传JPG/JPEG格式的图片"]);
    exit;
}

// 验证文件大小
if ($file_size > $max_size) {
    echo json_encode(["status" => "error", "message" => "图片大小不能超过5MB"]);
    exit;
}

// 生成唯一文件名,避免重名
$unique_name = uniqid() . '_' . $file_name;
// 上传目录,确保这个目录存在且有写入权限,比如放在项目根目录下的uploads文件夹
$upload_dir = 'uploads/';
if (!is_dir($upload_dir)) {
    mkdir($upload_dir, 0777, true);
}
$file_path = $upload_dir . $unique_name;

// 移动上传文件到目标目录
if (!move_uploaded_file($file['tmp_name'], $file_path)) {
    echo json_encode(["status" => "error", "message" => "图片保存失败"]);
    exit;
}

// 将图片信息存入数据库
$sql = "INSERT INTO flat_gallery (flat_name, building_name, location, image_path) VALUES (?, ?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("ssss", $flat_name, $building_name, $location, $file_path);

if ($stmt->execute()) {
    echo json_encode(["status" => "success", "message" => "图片上传成功"]);
} else {
    // 如果数据库插入失败,删除已上传的图片
    unlink($file_path);
    echo json_encode(["status" => "error", "message" => "数据库保存失败: " . $stmt->error]);
}

// 关闭连接
$stmt->close();
$conn->close();
?>

注意事项

  • 确保uploads目录存在,并且给服务器赋予写入权限(生产环境可调整为更安全的权限配置)
  • 修改数据库连接参数为你自己的数据库信息
  • 可根据需求调整允许的文件类型、最大文件大小
  • 前端的ApiConstant.flatuploadurl要指向这个PHP文件的访问地址

内容的提问来源于stack exchange,提问作者duaboola

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 05:35:18