Android端提交数据至MySQL出现重复插入行问题求助
问题分析与解决方案:MySQL插入重复行的原因及修复
首先,咱们来拆解你遇到的核心问题:两次HTTP请求触发了两次数据库插入,导致同一用户的数据被分成两行存入数据库。
问题根源拆解
- Android端的两次独立请求:你用
AsyncHttpClient提交文本数据,又用单独的HttpURLConnection代码上传语音文件,这是两个完全独立的HTTP请求,都会触发PHP脚本里的send()函数。 - PHP代码的无差别插入:不管当前请求是文本提交还是语音上传,
send()函数都会执行数据库插入操作:- 第一次请求(仅文本):没有上传文件,
$_FILES['uploaded_file']不存在,$file_path默认变成uploads/,插入后生成第一行数据。 - 第二次请求(仅语音):虽然上传了文件,但如果请求里附带了重复的文本参数(或者
$_REQUEST从URL/表单里拿到了之前的参数),就会再次执行插入,生成第二行数据。
- 第一次请求(仅文本):没有上传文件,
解决方案:合并请求(推荐)
最可靠的方式是把文本数据和语音文件放在同一个Multipart请求里一次性提交,这样PHP只处理一次插入,完美解决重复行问题。
1. 修改Android端代码:用AsyncHttpClient提交Multipart请求
替换原来的两次请求逻辑,把所有数据(文本+语音)打包成一个请求发送:
btn_send.setOnClickListener(v -> { // 获取表单输入的文本数据 String username = "你的用户名"; // 从SharedPreferences或其他地方获取 String title = new_banners_title.getText().toString().trim(); String desc = new_banners_desc.getText().toString().trim(); // 检查输入是否为空 if (title.isEmpty() || desc.isEmpty()) { Toast.makeText(this, "标题和描述不能为空", Toast.LENGTH_SHORT).show(); return; } // 构建Multipart请求 AsyncHttpPost post = new AsyncHttpPost(upLoadServerUri); MultipartFormDataBody body = new MultipartFormDataBody(); // 添加文本参数 body.addPart("Username", username); body.addPart("Title", title); body.addPart("Desc", desc); body.addPart("City", city); body.addPart("Cate", cate); // 添加语音文件 File voiceFile = new File(patch); // patch是你的语音文件本地路径 if (!voiceFile.exists()) { Toast.makeText(this, "语音文件不存在", Toast.LENGTH_SHORT).show(); return; } try { body.addPart("uploaded_file", voiceFile.getName(), null, new FileInputStream(voiceFile)); } catch (FileNotFoundException e) { e.printStackTrace(); Toast.makeText(this, "读取语音文件失败", Toast.LENGTH_SHORT).show(); return; } post.setBody(body); // 发送请求并处理响应 AsyncHttpClient.getDefaultInstance().executeString(post, (ex, response) -> { runOnUiThread(() -> { if (ex == null) { String result = response.getString(); if (result.contains("success") && result.contains("Data inserted successfully")) { Toast.makeText(New_Banners.this, "提交成功!", Toast.LENGTH_SHORT).show(); finish(); } else { Toast.makeText(New_Banners.this, "提交失败:" + result, Toast.LENGTH_SHORT).show(); } } else { ex.printStackTrace(); Toast.makeText(New_Banners.this, "网络连接失败", Toast.LENGTH_SHORT).show(); } }); return null; }); });
2. 修改PHP代码:完善逻辑+防护SQL注入
原代码存在SQL注入风险,且没有做参数校验,修改后的代码如下:
<?php send(); function connectToDatabase() { $connection = mysqli_connect("***", "***", "***", "***"); if (mysqli_connect_errno()) { echo "数据库连接失败: " . mysqli_connect_error(); exit; // 连接失败直接终止 } mysqli_set_charset($connection, "utf8"); return $connection; } function send() { // 校验必填参数是否存在 $requiredParams = ['Username', 'Title', 'Desc', 'City', 'Cate']; foreach ($requiredParams as $param) { if (!isset($_REQUEST[$param]) || empty(trim($_REQUEST[$param]))) { echo "缺少必填参数: $param"; return; } } $file_path = ""; // 处理文件上传(如果有) if (isset($_FILES['uploaded_file']) && $_FILES['uploaded_file']['error'] == UPLOAD_ERR_OK) { $uploadDir = "uploads/"; // 确保上传目录存在 if (!file_exists($uploadDir)) mkdir($uploadDir, 0755, true); $fileName = basename($_FILES['uploaded_file']['name']); // 可以添加文件类型校验,比如只允许mp3等音频格式 $allowedExts = ['mp3', 'wav', 'm4a']; $fileExt = strtolower(pathinfo($fileName, PATHINFO_EXTENSION)); if (!in_array($fileExt, $allowedExts)) { echo "不允许的文件格式"; return; } $file_path = $uploadDir . $fileName; if (!move_uploaded_file($_FILES['uploaded_file']['tmp_name'], $file_path)) { echo "文件上传失败"; return; } echo "文件上传成功; "; } // 连接数据库并插入数据(使用预处理语句防SQL注入) $connection = connectToDatabase(); $stmt = mysqli_prepare($connection, "INSERT INTO banners (Username, Title, Descript, City, Cate, Date, voice, Status) VALUES (?, ?, ?, ?, ?, ?, ?, ?)" ); $username = trim($_REQUEST['Username']); $title = trim($_REQUEST['Title']); $desc = trim($_REQUEST['Desc']); $city = trim($_REQUEST['City']); $cate = trim($_REQUEST['Cate']); $date = date("Y/m/d G:i"); $status = 1; // 绑定参数 mysqli_stmt_bind_param($stmt, "sssssssi", $username, $title, $desc, $city, $cate, $date, $file_path, $status); if (mysqli_stmt_execute($stmt)) { echo "数据插入成功"; } else { echo "数据插入失败: " . mysqli_error($connection); } mysqli_stmt_close($stmt); mysqli_close($connection); } ?>
额外注意事项
- SQL注入防护:原代码直接把用户输入拼到SQL里,非常危险,必须用预处理语句(上面的代码已经实现)。
- 文件上传安全:添加了文件格式校验,你还可以限制文件大小,避免恶意文件上传。
- 错误处理:完善了参数校验、数据库连接错误、文件上传错误的提示,方便排查问题。
内容的提问来源于stack exchange,提问作者alexis programmer
相关产品推荐
相关产品推荐

