织梦html5上传

99ANYc3cd6
预计阅读时长 50 分钟
位置: 首页 织梦建站 正文

织梦默认的上传功能比较传统,使用的是表单提交和iframe回传的方式,体验不佳,而HTML5上传(通常结合AJAX)可以实现无刷新、带进度条、更好的错误处理等现代化用户体验。

实现这个功能主要有两种思路:

  1. 完全自定义开发(推荐):完全绕过织梦默认的上传逻辑,自己编写前端HTML5代码和后端PHP处理脚本,然后将文件信息插入到织梦的数据库中,这种方式最灵活,但需要较多的代码量。
  2. 修改织梦核心文件(不推荐):修改织梦后台的media_add.php等文件,使其支持AJAX请求,这种方式风险高,升级织梦时容易失效,且可能破坏原有功能。

下面,我将重点讲解第一种思路,因为它更稳定、更符合现代开发规范,并且能让你更好地理解整个流程。


方案:完全自定义HTML5上传(以文章附件上传为例)

我们将创建一个独立的页面,用于上传文件,并将上传成功后的文件信息(路径、名称等)返回给主页面,再由主页面将其添加到文章的附件列表中。

第一步:准备数据库(织梦已做好)

织梦的附件信息存储在#@__uploads(或dede_uploads)表中,这个表包含了所有上传文件的路径、名称、大小、类型等信息,我们只需要将新上传的文件信息插入到这个表中即可。

第二步:创建前端上传页面 (upload.html)

这个页面包含一个拖拽区域、一个文件选择按钮和一个隐藏的iframe(用于处理织梦的formhash和可能的回传)。

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">HTML5 文件上传</title>
    <style>
        body { font-family: 'Microsoft YaHei', sans-serif; background-color: #f4f4f4; }
        .upload-container { max-width: 600px; margin: 50px auto; background: #fff; padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.1); }
        .upload-area {
            border: 2px dashed #ccc;
            border-radius: 5px;
            padding: 40px;
            text-align: center;
            cursor: pointer;
            transition: border-color 0.3s;
        }
        .upload-area:hover, .upload-area.drag-over {
            border-color: #007bff;
            background-color: #f8f9fa;
        }
        .upload-area p { margin: 10px 0; color: #666; }
        #fileInput { display: none; }
        .progress-bar {
            height: 10px;
            background-color: #e0e0e0;
            border-radius: 5px;
            margin-top: 15px;
            overflow: hidden;
        }
        .progress {
            height: 100%;
            background-color: #007bff;
            width: 0%;
            transition: width 0.3s;
        }
        #fileList { margin-top: 20px; }
        .file-item {
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px;
            border: 1px solid #eee;
            margin-bottom: 10px;
            border-radius: 4px;
        }
        .file-item .name { flex-grow: 1; }
        .file-item .size { color: #999; margin-right: 10px; }
        .file-item .status { color: #28a745; font-weight: bold; }
    </style>
</head>
<body>
    <div class="upload-container">
        <h2>上传附件</h2>
        <div class="upload-area" id="dropZone">
            <p>拖拽文件到此处,或</p>
            <button class="btn btn-primary">点击选择文件</button>
            <input type="file" id="fileInput" multiple>
        </div>
        <div class="progress-bar" id="progressBar" style="display: none;">
            <div class="progress" id="progress"></div>
        </div>
        <div id="fileList"></div>
    </div>
    <!-- 用于织梦表单验证的隐藏表单 -->
    <form id="dedeForm" action="/plus/file.php" method="post" style="display: none;">
        <input type="hidden" name="dopost" value="upload">
        <input type="hidden" name="arcid" value="0"> <!-- 文章ID,上传时为0,后续由JS处理 -->
        <input type="hidden" name="formhash" value="{dede:global.formhash /}"> <!-- 获取织梦的formhash -->
        <input type="hidden" name="isadmin" value="1">
        <input type="hidden" name="userid" value="{dede:global.userid /}">
        <input type="hidden" name="username" value="{dede:global.username /}">
    </form>
    <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
    <script>
        document.addEventListener('DOMContentLoaded', function() {
            const dropZone = document.getElementById('dropZone');
            const fileInput = document.getElementById('fileInput');
            const fileList = document.getElementById('fileList');
            const progressBar = document.getElementById('progressBar');
            const progress = document.getElementById('progress');
            const dedeForm = document.getElementById('dedeForm');
            // 点击上传区域触发文件选择
            dropZone.addEventListener('click', () => fileInput.click());
            // 监听文件选择
            fileInput.addEventListener('change', (e) => {
                handleFiles(e.target.files);
            });
            // 拖拽事件
            ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
                dropZone.addEventListener(eventName, preventDefaults, false);
            });
            function preventDefaults(e) {
                e.preventDefault();
                e.stopPropagation();
            }
            ['dragenter', 'dragover'].forEach(eventName => {
                dropZone.addEventListener(eventName, () => {
                    dropZone.classList.add('drag-over');
                }, false);
            });
            ['dragleave', 'drop'].forEach(eventName => {
                dropZone.addEventListener(eventName, () => {
                    dropZone.classList.remove('drag-over');
                }, false);
            });
            dropZone.addEventListener('drop', (e) => {
                const dt = e.dataTransfer;
                const files = dt.files;
                handleFiles(files);
            });
            // 处理文件上传
            function handleFiles(files) {
                if (files.length === 0) return;
                // 获取织梦的formhash,如果页面中没有,需要通过AJAX获取
                // 这里假设我们在一个织梦模板页面中,可以直接使用{dede:global.formhash /}
                const formhash = document.querySelector('input[name="formhash"]').value;
                // 遍历所有文件并上传
                Array.from(files).forEach(file => {
                    uploadFile(file, formhash);
                });
            }
            function uploadFile(file, formhash) {
                const formData = new FormData();
                formData.append('Filedata', file); // 注意:Filedata是织梦PHP脚本接收的默认字段名
                formData.append('dopost', 'upload');
                formData.append('formhash', formhash);
                formData.append('isadmin', '1');
                formData.append('userid', document.querySelector('input[name="userid"]').value);
                formData.append('username', document.querySelector('input[name="username"]').value);
                formData.append('arcid', '0'); // 初始为0,成功后由JS更新
                const item = document.createElement('div');
                item.className = 'file-item';
                item.innerHTML = `
                    <span class="name">${file.name}</span>
                    <span class="size">${(file.size / 1024).toFixed(2)} KB</span>
                    <span class="status">上传中...</span>
                `;
                fileList.appendChild(item);
                progressBar.style.display = 'block';
                axios.post('/plus/file.php', formData, {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    },
                    onUploadProgress: progressEvent => {
                        const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
                        progress.style.width = percentCompleted + '%';
                    }
                })
                .then(response => {
                    // 织梦上传成功后,PHP会返回JSON格式的数据
                    // 格式如: {"error":0,"message":"success","url":"uploads/xxx.jpg"}
                    const result = response.data;
                    if (result.error === 0) {
                        item.querySelector('.status').textContent = '上传成功';
                        item.querySelector('.status').style.color = '#28a745';
                        // 这里可以触发一个自定义事件,将文件信息传递给父页面
                        window.parent.postMessage({
                            type: 'fileUploaded',
                            data: result
                        }, '*'); // 注意:在生产环境中,不要使用 '*',指定具体域名
                    } else {
                        item.querySelector('.status').textContent = `上传失败: ${result.message}`;
                        item.querySelector('.status').style.color = '#dc3545';
                    }
                })
                .catch(error => {
                    item.querySelector('.status').textContent = '上传失败: 网络错误';
                    item.querySelector('.status').style.color = '#dc3545';
                    console.error('Upload error:', error);
                })
                .finally(() => {
                    progress.style.width = '0%';
                    setTimeout(() => {
                        progressBar.style.display = 'none';
                    }, 1000);
                });
            }
        });
    </script>
</body>
</html>

第三步:修改织梦后端处理文件 (/plus/file.php)

织梦的/plus/file.php文件是用来处理文件上传的核心,我们需要修改它,让它能正确响应AJAX请求,并返回JSON格式的数据。

注意:在修改核心文件前,请务必备份原文件!

打开 /plus/file.php,找到类似 ShowMsg('上传文件成功!', '-1'); 这样的代码,将其修改为返回JSON。

// 在文件的开头,确保开启了输出缓冲
ob_start();
// ... (其他代码,包括权限检查等) ...
// 假设文件上传成功后的代码段
// 原来的代码可能是这样:
// $filename = $cfg_upload_path.'/'.$filename;
// if(!move_uploaded_file($uploadfile, $filename))
// {
//     ShowMsg('上传文件失败!', '-1');
//     exit();
// }
// $dsql->ExecuteNoneQuery("INSERT INTO `#@__uploads`(arcid,title,uptime,filepath,filesize) VALUES ('$arcid','$filename','$mtime','$filename','$filesize');");
// ShowMsg('上传文件成功!', '-1');
// 修改为:
$filename = $cfg_upload_path.'/'.$filename;
if(!move_uploaded_file($uploadfile, $filename))
{
    // 返回JSON错误信息
    header('Content-Type: application/json');
    echo json_encode(['error' => 1, 'message' => '上传文件失败!']);
    exit();
}
// 插入数据库
$dsql->ExecuteNoneQuery("INSERT INTO `#@__uploads`(arcid,title,uptime,filepath,filesize) VALUES ('$arcid','$filename','$mtime','$filename','$filesize');");
// 返回JSON成功信息
$return_data = [
    'error' => 0,
    'message' => 'success',
    'url' => $filename, // 返回文件的相对路径
    'filename' => $filename, // 返回完整路径 => $filename, // 返回文件名
    'filesize' => $filesize
];
header('Content-Type: application/json');
echo json_encode($return_data);
exit();

关键点

  1. header('Content-Type: application/json');:告诉浏览器返回的是JSON数据。
  2. json_encode():将PHP数组转换为JSON字符串。
  3. exit():在输出JSON后立即终止脚本执行,防止织梦原有的HTML代码被输出。

第四步:在文章编辑页面调用上传组件

你需要在一个织梦模板页面(例如文章编辑页 article_add.php 的模板)中嵌入这个上传组件,并监听它发送的消息。

  1. 引入上传组件:在文章编辑页的模板文件中,使用{dede:include filename='upload.html'/}或iframe来引入我们创建的upload.html
  2. 监听消息:在文章编辑页的JavaScript代码中,监听message事件。
// 在文章编辑页的JS中
window.addEventListener('message', function(event) {
    // 安全检查:检查消息来源是否可信
    // if (event.origin !== "https://your-dede-domain.com") {
    //     return;
    // }
    if (event.data.type === 'fileUploaded') {
        const fileInfo = event.data.data;
        console.log('文件上传成功:', fileInfo);
        // 在这里调用织梦的JS函数,将附件添加到编辑器中
        // 这需要你查看织梦编辑器(如ckeditor)的API
        // 如果是DedeCMS 5.7/5.8的版本,可能会有类似下面的函数
        try {
            // 假设织梦有一个全局函数 addAttachmentToEditor
            // addAttachmentToEditor(fileInfo.url, fileInfo.title);
            // 或者,直接操作DOM,将附件信息添加到附件列表
            const attachmentList = document.getElementById('myAttachmentList'); // 假设你有一个附件列表容器
            const newItem = document.createElement('div');
            newItem.innerHTML = `<input type="text" name="附件[]" value="${fileInfo.title}" readonly> <input type="hidden" name="附件路径[]" value="${fileInfo.url}">`;
            attachmentList.appendChild(newItem);
            // 显示成功提示
            alert('附件 "' + fileInfo.title + '" 添加成功!');
        } catch (e) {
            console.error('添加附件到编辑器失败:', e);
        }
    }
});

总结与注意事项

  1. 安全性:修改核心文件有风险,确保你理解代码逻辑,并在修改前备份,在生产环境中,对上传的文件类型、大小、名称进行严格的过滤和检查,防止上传恶意文件。
  2. 兼容性:HTML5的FileReaderFormData在现代浏览器中支持良好,但需要考虑旧浏览器的兼容性问题。
  3. 织梦版本:不同版本的织梦,其file.php和编辑器API可能有所不同,需要根据实际情况调整代码。
  4. 用户体验:这个方案提供了很好的上传体验,但与织梦原有的附件管理流程是解耦的,你需要自己处理附件与文章的关联(即arcid字段),在上传成功后,将返回的文件信息(url, title等)手动添加到文章的附件表单中,以便在保存文章时能正确关联。

这个自定义的HTML5上传方案虽然步骤稍多,但它为你提供了完全的控制权,并且能够实现一个现代化、用户友好的上传界面,是提升织梦后台体验的一个有效途径。

-- 展开阅读全文 --
头像
C语言如何生成不重复的随机数?
« 上一篇 今天
C语言如何大家一起做游戏?
下一篇 » 今天

相关文章

取消
微信二维码
支付宝二维码

目录[+]