Flutter Dio多图+文本提交PHP后端:解决数据库重复行问题
问题描述
使用Flutter的Dio组件向PHP后端提交包含文本字段与多张图片的表单时,当前代码会为每张图片在MySQL数据库中生成一条记录,需实现单条记录存储文本字段及图片名的JSON数组,以下是修正方案:
Flutter 代码修正
现有问题代码
Future postOperation( BuildContext context, String operationType, String operation, String description, ) async { try { var uuid = const Uuid(); final user = await context.read<UserProvider>().getUser(); final imageList = context.read<OperationProvider>().images; for (var image in imageList) { MultipartFile multipartFile = MultipartFile.fromFileSync( image.path, contentType: MediaType('image', image.path.split('.').last), filename: '${uuid.v1()}.${image.path.split('.').last}', ); FormData formData = FormData.fromMap({ 'user_id': user.userId!.trim(), 'palace_id': user.palaceId!.trim(), 'operation_type': operationType.trim(), 'operation': operation.trim(), 'description': description.trim(), "media": multipartFile, }); var response = await Dio().post( AppConst.operation, data: formData, ); if (response.statusCode == 200) { print('Operation uploaded - server response: ${response.statusCode}'); } else { print( 'Operation not uploaded - server response: ${response.statusCode}'); } } } catch (error) { print(error); } }
修正后代码
Future postOperation( BuildContext context, String operationType, String operation, String description, ) async { try { var uuid = const Uuid(); final user = await context.read<UserProvider>().getUser(); final imageList = context.read<OperationProvider>().images; // 批量生成所有图片的MultipartFile对象 List<MultipartFile> mediaFiles = []; for (var image in imageList) { String fileName = '${uuid.v1()}.${image.path.split('.').last}'; MultipartFile multipartFile = MultipartFile.fromFileSync( image.path, contentType: MediaType('image', image.path.split('.').last), filename: fileName, ); mediaFiles.add(multipartFile); } // 一次性提交所有表单数据(含多张图片) FormData formData = FormData.fromMap({ 'user_id': user.userId!.trim(), 'palace_id': user.palaceId!.trim(), 'operation_type': operationType.trim(), 'operation': operation.trim(), 'description': description.trim(), "media": mediaFiles, // 传递图片列表而非单张图片 }); var response = await Dio().post( AppConst.operation, data: formData, ); if (response.statusCode == 200) { print('Operation uploaded - server response: ${response.statusCode}'); } else { print('Operation not uploaded - server response: ${response.statusCode}'); } } catch (error) { print(error); } }
修正点:
- 移除循环内的重复请求,改为批量处理图片后一次性提交表单
- 将图片列表作为
media字段值传递,避免多次请求生成多条数据库记录
PHP 后端代码修正
现有问题代码
<?php require_once "../../conn/conn.php"; if ($_POST['user_id']) { $base = $_SERVER['REQUEST_SCHEME']."://".$_SERVER['SERVER_NAME']; $path = "/media/operation/"; $domanin = "$base$path"; $user_id = mysqli_real_escape_string($connect, $_POST['user_id']); $palace_id = mysqli_real_escape_string($connect, $_POST['palace_id']); $operation_type = mysqli_real_escape_string($connect, $_POST['operation_type']); $operation = mysqli_real_escape_string($connect, $_POST['operation']); $description = mysqli_real_escape_string($connect, $_POST['description']); $image[] = $_FILES['media']['name']; $tmpFile[] = $_FILES['media']['tmp_name']; foreach ($image as $key => $value) { foreach ($tmpFile as $key => $tmpFileValue) { move_uploaded_file($tmpFileValue, '../../media/operation/'.$value); } } $media = "$domanin$value"; $encode = json_encode($media); $query = " INSERT INTO operation (palace_id, user_id, operation_type, operation, description, media) VALUES ('$palace_id', '$user_id', '$operation_type', '$operation', '$description', '$encode') "; $result = mysqli_query($connect, $query); mysqli_close($connect); } ?>
修正后代码
<?php require_once "../../conn/conn.php"; if ($_POST['user_id']) { $base = $_SERVER['REQUEST_SCHEME']."://".$_SERVER['SERVER_NAME']; $path = "/media/operation/"; $domain = "$base$path"; $user_id = mysqli_real_escape_string($connect, $_POST['user_id']); $palace_id = mysqli_real_escape_string($connect, $_POST['palace_id']); $operation_type = mysqli_real_escape_string($connect, $_POST['operation_type']); $operation = mysqli_real_escape_string($connect, $_POST['operation']); $description = mysqli_real_escape_string($connect, $_POST['description']); $mediaUrls = []; // 处理多文件上传的数组格式 if (isset($_FILES['media']) && is_array($_FILES['media']['name'])) { $fileNames = $_FILES['media']['name']; $tmpNames = $_FILES['media']['tmp_name']; $errors = $_FILES['media']['error']; foreach ($fileNames as $index => $fileName) { if ($errors[$index] == UPLOAD_ERR_OK) { $tmpName = $tmpNames[$index]; // 移动文件到目标目录 move_uploaded_file($tmpName, '../../media/operation/'.$fileName); // 拼接完整图片URL并加入数组 $mediaUrls[] = $domain . $fileName; } } } // 将图片URL数组转为JSON字符串 $mediaJson = json_encode($mediaUrls); // 使用预处理语句防止SQL注入 $query = "INSERT INTO operation (palace_id, user_id, operation_type, operation, description, media) VALUES (?, ?, ?, ?, ?, ?)"; $stmt = mysqli_prepare($connect, $query); mysqli_stmt_bind_param($stmt, "ssssss", $palace_id, $user_id, $operation_type, $operation, $description, $mediaJson); mysqli_stmt_execute($stmt); mysqli_stmt_close($stmt); mysqli_close($connect); } ?>
修正点:
- 适配多文件上传的数组格式,遍历处理每张图片
- 收集所有图片URL到数组后转为JSON字符串存储,实现单条记录存储多图信息
- 使用MySQL预处理语句替代直接拼接SQL,避免SQL注入风险
- 修复原代码中双层循环重复移动文件的错误
最终数据库存储效果
修正后,operation表的单条记录格式如下:
{ "user_id": "1", "palace_id": "1", "operation_type": "Operation Type", "operation": "Operation", "description": "Description", "media": [ "http://your-domain/media/operation/66b36250-7cf4-11ed-8311-e1017d35fc4f.jpg", "http://your-domain/media/operation/6g09e250-7cf4-1emd-8311-e1017cb3934f.jpg" ] }
内容的提问来源于stack exchange,提问作者Federico
相关产品推荐
相关产品推荐

