PHP开发类云盘系统:文件转二进制存入数据库报错求助
云存储系统文件转二进制存入数据库问题排查与解决
问题概述
正在用PHP+HTML开发类Google Drive的云存储系统,目标流程:
- 用户通过HTML表单上传文件 → 转二进制格式 → 存入数据库
- 用户点击按钮下载 → 二进制转回原文件
当前上传时触发错误,报错信息:
Fatal error: Uncaught TypeError: fopen(): Argument #1 ($filename) must be of type string, array given in C:\xampp\htdocs\fileshub\src\send_file.php:12 Stack trace: #0 C:\xampp\htdocs\fileshub\src\send_file.php(12): fopen(Array, 'rb') #1 {main} thrown in C:\xampp\htdocs\fileshub\src\send_file.php on line 12
现有代码
HTML上传表单
<form class="upload-form" action="./src/send_file.php" method="post" enctype="multipart/form-data"><br> <input type="text" name="filename" id="filename" placeholder="File name"> <input type="file" name="file" id="file"><br> <button type="submit" class="submit">Submit</button> </form>
(注:原代码中按钮type值为笔误sumbit,已修正为submit)
PHP处理脚本(send_file.php)
<?php session_start(); include "../src/db_conn.php"; if(isset($_SESSION['id'])) { $id = $_SESSION['id']; // 用户ID $filename = $_POST['filename']; // 文件名 $file = $_FILES['file']; $data = fopen ($file, 'rb'); $size = filesize ($file); $contents = fread ($data, $size); fclose ($data); $binfile = base64_encode($contents); if(!$filename|| !$file) { header("Location: ../index.php?error=Enter your data!"); exit(); } else { $check = "SELECT bin_code FROM files WHERE user_id = '$id' AND bin_code = '$binfile' AND file_name = '$filename'"; $result = mysqli_query($conn, $check); if(mysqli_num_rows($result) === 1){ header("Location: ../index.php?error=Your file exsist."); exit(); }else { $save = "INSERT INTO files (user_id, file_name, bin_code) values('$id', '$filename', $binfile)"; $saveresult = mysqli_query($conn, $save); $saveresult; header("Location: ../index.php?error=Your file has been saved"); exit(); } } } ?>
数据库连接文件(db_conn.php)
<?php $server = "localhost"; $user ="root"; $password = ""; $db = "fileshub"; $conn = mysqli_connect($server, $user, $password, $db); ?>
数据库表说明:
- Files表:存储用户文件数据,包含
user_id、file_name、bin_code字段 - Users表:存储用户基础信息,包含用户ID字段
问题分析与解决方案
1. 核心错误原因
$_FILES['file']是一个数组,包含上传文件的临时路径、大小、类型等元数据,而fopen()需要传入字符串类型的文件路径。直接将数组传入fopen()会触发类型错误,需改用$_FILES['file']['tmp_name']获取上传文件的临时存储路径。
2. 修正后的完整PHP脚本
<?php session_start(); include "../src/db_conn.php"; // 检查数据库连接状态 if (!$conn) { die("数据库连接失败: " . mysqli_connect_error()); } if(isset($_SESSION['id'])) { $id = $_SESSION['id']; // 用户未输入文件名时,自动使用上传文件的原始名称 $filename = !empty($_POST['filename']) ? $_POST['filename'] : $_FILES['file']['name']; $uploadedFile = $_FILES['file']; // 校验文件是否上传成功 if ($uploadedFile['error'] !== UPLOAD_ERR_OK) { header("Location: ../index.php?error=文件上传失败,错误码: " . $uploadedFile['error']); exit(); } // 校验文件名和文件是否存在 if(empty($filename) || empty($uploadedFile['tmp_name'])) { header("Location: ../index.php?error=请输入文件名并选择文件!"); exit(); } // 读取文件内容并转base64编码 $tmpFilePath = $uploadedFile['tmp_name']; $contents = file_get_contents($tmpFilePath); // 替代fopen+fread,简化代码 $binfile = base64_encode($contents); // 用预处理语句避免SQL注入,检查文件是否已存在 $checkStmt = mysqli_prepare($conn, "SELECT bin_code FROM files WHERE user_id = ? AND file_name = ?"); mysqli_stmt_bind_param($checkStmt, "is", $id, $filename); mysqli_stmt_execute($checkStmt); mysqli_stmt_store_result($checkStmt); if(mysqli_stmt_num_rows($checkStmt) > 0){ header("Location: ../index.php?error=该文件名已存在!"); mysqli_stmt_close($checkStmt); exit(); } // 插入数据库,使用预处理语句 $saveStmt = mysqli_prepare($conn, "INSERT INTO files (user_id, file_name, bin_code) VALUES (?, ?, ?)"); mysqli_stmt_bind_param($saveStmt, "iss", $id, $filename, $binfile); if(mysqli_stmt_execute($saveStmt)){ header("Location: ../index.php?success=文件已成功保存"); } else { header("Location: ../index.php?error=保存失败: " . mysqli_error($conn)); } // 释放资源 mysqli_stmt_close($saveStmt); mysqli_close($conn); } else { header("Location: ../index.php?error=请先登录!"); exit(); } ?>
3. 优化点说明
- SQL注入防护:替换原SQL拼接逻辑,使用预处理语句绑定参数,彻底避免注入风险
- 上传校验增强:增加文件上传错误码检查,覆盖文件上传失败场景
- 容错处理:用户未输入文件名时自动使用原始文件名,提升体验
- 代码简化:用
file_get_contents()替代fopen()+fread(),减少冗余代码 - 错误提示优化:返回更明确的错误信息,便于调试
- 资源管理:及时关闭预处理语句和数据库连接,避免资源泄漏
4. 下载功能实现思路
用户点击下载按钮时,执行以下步骤:
- 根据用户ID和文件名从数据库取出
bin_code字段值 - 对
bin_code执行base64_decode()解码,还原文件二进制内容 - 设置HTTP响应头,指定文件类型和下载文件名:
header('Content-Type: application/octet-stream'); header('Content-Disposition: attachment; filename="' . $filename . '"'); header('Content-Length: ' . strlen($decodedContent)); - 输出解码后的二进制内容,完成文件下载
内容的提问来源于stack exchange,提问作者Qbiter
相关产品推荐
相关产品推荐

