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

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. 下载功能实现思路

用户点击下载按钮时,执行以下步骤:

  1. 根据用户ID和文件名从数据库取出bin_code字段值
  2. 对bin_code执行base64_decode()解码,还原文件二进制内容
  3. 设置HTTP响应头,指定文件类型和下载文件名:
    header('Content-Type: application/octet-stream');
    header('Content-Disposition: attachment; filename="' . $filename . '"');
    header('Content-Length: ' . strlen($decodedContent));
    
  4. 输出解码后的二进制内容,完成文件下载

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 08:25:20