基于PHP的文件存储系统

PHP文件存储系统

【功能分析】

一个简单的文件存储系统具体功能需求:

  • 编写文件上传表单

  • 处理文件上传,限制允许上传的文件类型,自动生成文件名,将文件保存到uploads目录

  • 展示上传文件列表,给每个文件添加下载链接,实现单击文件名下载文件

  • 实现下载功能

【功能实现】

1) 创建文件上传表单

<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8" />
    <title>文件存储系统</title>
</head>

<body>
    <p>上传文件:</p>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="upload" />
        <input type="submit" name="upload" value="上传" />
    </form>
<!--文件列表-->
</body>
</html>

2)PHP处理文件上传

// 上传功能
if (isset($_FILES['upload'])) {
    if ($_FILES['upload']['error'] !== UPLOAD_ERR_OK) {
        $response = array(
            'status' => 'error',
            'message' => '发生错误,上传失败'
        );
        echo json_encode($response);
        exit;
    }
    $suffix = strtolower(pathinfo($_FILES['upload']['name'], PATHINFO_EXTENSION)); // 获取文件后缀并转换为小写
    $allow_type = [
        "jpeg", "png", "gif", "bmp", "jpg",
        "doc", "docx", "pdf", "txt", "md",
        "xls", "xlsx", "csv",
        "zip", "rar", "7z",
        "mp3", "wav", "aac", "flac", "mp4", "avi", "mov", "wmv",
        "exe", "msi",
        "db", "dbf", "mdb", "json", "xml"
    ];
    if (!in_array($suffix, $allow_type)) {
        exit('不允许的文件类型。');
    }
    $filename = $_FILES['upload']['name']; // 获取上传文件的原始文件名
    $destination = './uploads/' . $filename; // 构建保存文件的目标路径
    if (is_uploaded_file($_FILES['upload']['tmp_name'])) {
        if (move_uploaded_file($_FILES['upload']['tmp_name'], $destination)) {
            $response = array(
                'status' => 'success',
                'message' => '上传成功'
            );
            echo json_encode($response);
            exit;
        }
    }
}

3)显示文件列表

$path = './uploads';
$handle = opendir($path);
while (($file = readdir($handle)) !== false) {
    if ($file == '.' || $file == '..') {
        continue;
    }
    $file_list[] = $file;
}
// print_r($file_list);
closedir($handle);

在页面输出文件列表

<p>文件列表:</p>
    <ul>
        <?php if (empty($file_list)) : ?>
            <li>目录为空</li>
        <?php else : foreach ($file_list as $v) : ?>
                <li>
                    <a href="?download=<?php echo urlencode($v); ?>">
                        <?php echo $v; ?>
                    </a>
                </li>
        <?php endforeach;
        endif; ?>
    </ul>

4)实现文件下载

$download = isset($_GET['download']) ? $_GET['download'] : '';
if ($download !== '') {
    $file_path = './uploads/' . $download;
    if (!file_exists($file_path)) {
        exit('文件不存在') ;
    }
    $file_name = basename($file_path);    //获取文件名称
    $file_size = filesize($file_path);    //获取文件大小
    header('Content-Type: application/octet-stream');
    header('Content-Disposition:attachment;filename="' . $file_name . '"');
    $buffer = 1024;
    $file_count = 0;
    $handle = fopen($file_path, 'r');
    while (!feof($handle) && ($file_size - $file_count > 0)) {
        echo fread($handle, $buffer);
        $file_count += $buffer;
    }
    fclose($handle);
    exit;
}

5)文件删除模块

$delete = isset($_GET['delete']) ? $_GET['delete'] : '';
if ($delete !== '') {
    $file_path = './uploads/' . $delete;
    if (file_exists($file_path)) {
        unlink($file_path); // 删除文件
        header('Location: ./upload.php'); // // 重定向刷新页面
        exit;
    } else {
        header('Location: ./upload.php'); // // 重定向刷新页面
        exit;
    }
}

在输出列表添加删除链接

<a href="?delete=<?php echo urlencode($v); ?>" onclick="return confirm('确认删除该文件吗?')">删除</a>

前往PHP文件存储系统

问题汇总

1)打开PHP文件时死循环,检查时发现找不到文件

原因:光创建了文件夹打包上传时会因为是空文件夹就忽略上传导致找不到文件夹

2)上传文件时报错打不开流

原因:因为我的网站用的是root用户,而文件夹是git用户文件权限是755,导致root用户可以读和执行却不能写入

解决方法:chmod 777 ./uploads/

3)解析域名不能及时更新上传与删除

原因:因为解析域名十分钟更新一次,暂时没有找到解决方法

小知识

1、回到博客页的各种方法

方法1)点击按钮返回上一页(只是加载上一页的缓存数据,并不会刷新页面,所以数据变动什么的不会生效)

<a href="javascript:history.back(-1)"><button class="back">回到博客</button></a>

方法2)方法1的优化版本

<input type="button" value="回到博客" οnclick="javascript:window.location='链接页面地址'">

2、linux文件权限小知识

在linux系统中ll查看文件你会发现类似于drwxrwxrwx的权限标识。这个标识由10个字符串组成,分为四个部分:

  1. 第一个字符:它代表文件类型或目录类型。常见的类型包括:

    • -:普通文件
    • d:目录
    • l:符号链接文件
    • c:字符设备文件
    • b:块设备文件
    • s:套接字文件
    • p:命名管道(FIFO)
  2. 后续的九个字符:它们用于表示权限。每三个字符一组,每组代表所有者(owner)、组用户(group)和其他用户(others)的权限。

    • 第一组(所有者权限):rwx 表示所有者的读、写和执行权限。
    • 第二组(组用户权限):rwx 表示组用户的读、写和执行权限。
    • 第三组(其他用户权限):rwx 表示其他用户的读、写和执行权限。

    在每组中,r 表示读权限,w 表示写权限,x 表示执行权限。如果某个位置上的字符是 -,表示相应的权限被禁止。
    例如,rwxrwxrwx 表示所有者、组用户和其他用户都具有读、写和执行的权限。
    值得注意的是,如果文件或目录没有某个权限,相应位置上的字符会被表示为 -,例如 drwxr-xr-- 表示所有者具有读、写和执行权限,组用户具有读和执行权限,其他用户仅具有读权限。

这些权限字符组合可以使用数字来表示,其中每个权限对应一个数字值:

  • r(读权限):4
  • w(写权限):2
  • x(执行权限):1

你可以将每个权限字符的数字值相加,以得到权限的数字表示。例如,rwxr-xr-- 可以转换为数字表示为 754,其中:

  • 所有者权限为 rwx,对应数字 7(4 + 2 + 1)。
  • 组用户权限为 r-x,对应数字 5(4 + 0 + 1)。
  • 其他用户权限为 r--,对应数字 4(4 + 0 + 0)。

因此,drwxr-xr-- 可以用数字表示为 754

3、上传以及删除刷新重复提交导致bug

原因: 提交后以及删除后URL并没有消失,刷新文件列表会导致重复执行

单纯的用header("Refresh:0");来刷新页面是不行的

解决方法: 需要通过设置header('Location: ./upload.php');来重定向页面这样就能刷新URL

4、上传文件过大导致失败

原因: Nginx的默认大小只允许50m大小的文件,PHP的默认文件上传大小只有20m

解决方法: 在Nginx与PHP的配置文件中修改一下即可

番外:美化

1、进度条实现

<script>
        $(document).ready(function() {
            $('#upload-form').submit(function(e) {
                e.preventDefault();
                var formData = new FormData(this);

                $.ajax({
                    url: 'upload.php',
                    type: 'POST',
                    data: formData,
                    dataType: 'json',
                    contentType: false,
                    processData: false,
                    xhr: function() {
                        var xhr = new window.XMLHttpRequest();
                        xhr.upload.addEventListener('progress', function(evt) {
                            if (evt.lengthComputable) {
                                var percent = (evt.loaded / evt.total) * 100;
                                $('.progress').css('width', percent + '%');
                                $('.progress-text').text(percent.toFixed(2) + '%');
                            }
                        }, false);
                        return xhr;
                    },
                    success: function(response) {
                        if (response.status === 'success') {

                            console.log(response.message);
                            location.reload(); // 刷新页面
                        } else {
                            console.log(response.message);
                        }
                    },
                    error: function(xhr, status, error) {
                        console.log(error);
                    }
                });
            });
        });
</script>

各配置详解:

  1. $(document).ready(function() { ... }): 这是jQuery的文档就绪事件,它指定在文档加载完成后执行的函数。在这个函数内部,定义了处理文件上传的逻辑。

  2. $('#upload-form').submit(function(e) { ... }): .submit() 函数用于指定表单提交时触发的处理函数。当表单被提交时,阻止默认的表单提交行为,以便使用AJAX进行异步文件上传。

  3. e.preventDefault();: 这是阻止事件默认行为的方法。在这种情况下,阻止了表单的默认提交行为,以便使用AJAX进行异步上传。

  4. var formData = new FormData(this);: 创建一个新的FormData对象,用于将表单数据存储起来。this 表示当前被提交的表单。

  5. $.ajax({ ... }): 这是使用jQuery的 ajax() 函数发起异步请求的方法。在这个代码块内部,我们配置了AJAX请求的各个参数,包括URL、请求类型、数据、数据类型等。

  6. xhr: function() { ... }: 这是通过 xhr 参数配置XHR对象的方法。在这里,通过 new window.XMLHttpRequest() 创建一个新的XHR对象,并为其添加了一个 upload 事件监听器。该事件监听器用于在上传过程中跟踪进度。

  7. xhr.upload.addEventListener('progress', function(evt) { ... }, false);: 这是 upload 事件的监听器。当上传进度发生变化时,该函数会被调用。通过计算已上传和总大小的比例,可以得到上传的百分比,并更新进度条的宽度和进度文本。

  8. success: function(response) { ... }: 这是请求成功时的回调函数。当服务器返回响应并成功处理请求时,该函数会被调用。使用 location.reload() 方法刷新页面。

  9. error: function(xhr, status, error) { ... }: 这是请求失败时的回调函数。当请求无法完成或返回错误状态时,该函数会被调用。

2、下载加速

使用cdn(内容分发网络)来给下载加速

优化后代码:

function downloadFile($file_path)
{
    // 强制下载文件
    header("Cache-Control: public");
    header("Content-Description: File Transfer");
    header("Content-Disposition: attachment; filename=" . basename($file_path));
    header("Content-Type: application/octet-stream");
    header("Content-Transfer-Encoding: binary");
    header('Location: ' . $file_path);
    exit;
}

// ...

// 下载文件
$download = isset($_GET['download']) ? $_GET['download'] : '';
if ($download !== '') {
    $file_path = 'http://qixiaorantut.top/php/uploads/' . $download;
    if (!file_exists($file_path)) {
        downloadFile($file_path);
    } else {
        exit('文件不存在');
    }
}

欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1701220998@qq.com
导航页 GitHub