whttpserver:一个命令极速搭建文件上传与下载服务器

whttpserver 是一个简单的HTTP服务器,类似于python -m http.server,但增加了文件上传和编辑的功能。

1. 安装 whttpserver 模块
python 复制代码
# 临时设置环境变量 PYTHONUTF8=1,强制 Python 使用 UTF-8 编码
set PYTHONUTF8=1
pip install whttpserver
2. 启动服务
python 复制代码
whttpserver --port <服务器监听的端口号,默认为25000> --dir <"文件上传和下载的根目录"> --debug <True or False>

启动信息如下:

在浏览器地址栏中,输入 http://127.0.0.1:8181/

3. 定制模板

可以看到,默认的页面比较简陋,我们可以修改下模板文件,让它看起来更美观一些。

模板文件路径为:%python安装目录%/Lib/site-packages/whttpserver/templates/index.html

修改后的 index.html 文件内容如下:

html 复制代码
<!DOCTYPE html>
<html lang="zh">
<head>
    <title>Directory listing for {{the_path}}</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }
        
        body {
            font-family: "Microsoft YaHei", "微软雅黑", Arial, sans-serif;
            background: linear-gradient(135deg, #f5f7fa 0%, #e4edf5 100%);
            color: #2c3e50;
            min-height: 100vh;
            padding: 20px;
            overflow: hidden; /* 确保整个页面无滚动条 */
            display: flex;
            flex-direction: column;
        }
        
        .header {
            display: flex;
            justify-content: space-between;
            align-items: center;
            margin-bottom: 25px;
            padding-bottom: 15px;
            border-bottom: 1px solid rgba(44, 62, 80, 0.1);
        }
        
        .breadcrumb {
            display: flex;
            align-items: center;
            gap: 8px;
            font-size: 18px;
        }
        
        .breadcrumb a {
            color: #3498db;
            text-decoration: none;
            transition: all 0.3s ease;
            padding: 5px 10px;
            border-radius: 4px;
        }
        
        .breadcrumb a:hover {
            background: rgba(52, 152, 219, 0.1);
            transform: translateY(-2px);
        }
        
        .breadcrumb span {
            color: #7f8c8d;
        }
        
        .upload-section {
            background: white;
            border-radius: 12px;
            padding: 20px;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
            margin-bottom: 25px;
        }
        
        .upload-form {
            display: flex;
            gap: 15px;
            align-items: center;
        }
        
        .file-input {
            flex: 1;
            padding: 12px 15px;
            border: 2px dashed #3498db;
            border-radius: 8px;
            background: rgba(52, 152, 219, 0.05);
            font-size: 16px;
            cursor: pointer;
        }
        
        .refresh-btn {
            background: #009688;
            color: white;
            border: none;
            padding: 12px 25px;
            border-radius: 8px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 4px 10px rgba(52, 152, 219, 0.3);
        }
		
		.upload-btn {
            background: #3498db;
            color: white;
            border: none;
            padding: 12px 25px;
            border-radius: 8px;
            font-weight: 600;
            cursor: pointer;
            transition: all 0.3s ease;
            box-shadow: 0 4px 10px rgba(52, 152, 219, 0.3);
        }
        
        .upload-btn:hover {
            background: #2980b9;
            transform: translateY(-2px);
            box-shadow: 0 6px 15px rgba(52, 152, 219, 0.4);
        }
        
        .messages {
            margin-top: 15px;
            padding: 10px;
            border-radius: 6px;
            background: rgba(46, 204, 113, 0.1);
            color: #27ae60;
            display: none;
        }
        
        .uploading-message {
            margin-top: 10px;
            color: #3498db;
            font-style: italic;
        }
        
        .table-container {
            background: white;
            border-radius: 12px;
            overflow: hidden;
            box-shadow: 0 4px 20px rgba(0, 0, 0, 0.05);
            flex: 1;
            display: flex;
            flex-direction: column;
            max-height: calc(100vh - 300px); /* 确保表格高度适应屏幕 */
        }
        
        .table-header {
            padding: 20px;
            border-bottom: 1px solid rgba(44, 62, 80, 0.1);
        }
        
        .table-header h3 {
            font-size: 22px;
            color: #2c3e50;
        }
        
        .table-scroll {
            overflow-y: auto;
            max-height: calc(100vh - 320px); /* 确保表格内容不超出视口 */
        }
        
        /* 隐藏滚动条但保留功能 */
        .table-scroll::-webkit-scrollbar {
            width: 0;
            height: 0;
        }
        
        table {
            width: 100%;
            border-collapse: separate;
            border-spacing: 0;
            font-size: 15px;
        }
        
        th {
            background: #f8f9fa;
            color: #7f8c8d;
            font-weight: 600;
            text-align: left;
            padding: 15px 20px;
            position: sticky;
            top: 0;
            border-bottom: 2px solid #ecf0f1;
        }
        
        td {
            padding: 12px 20px;
            border-bottom: 1px solid #ecf0f1;
        }
        
        tr:hover td {
            background: rgba(236, 240, 241, 0.5);
        }
        
        .file a {
            color: #27ae60;
            text-decoration: none;
            font-weight: 500;
            transition: all 0.2s ease;
        }
        
        .dir a {
            color: #3498db;
            text-decoration: none;
            font-weight: 500;
            transition: all 0.2s ease;
        }
        
        .file a:hover, .dir a:hover {
            text-decoration: underline;
            opacity: 0.9;
        }
        
        .footer {
            text-align: center;
            padding: 20px;
            color: #7f8c8d;
            font-size: 14px;
            margin-top: 20px;
            border-top: 1px solid rgba(44, 62, 80, 0.1);
        }
    </style>
</head>
<body>
    <div class="header">
        <div class="breadcrumb">
            <a href="/">根目录</a>
            <span>›</span>
            <a href="{{ parent_path }}">上一级</a>
        </div>
		<h3><input type="button" value="刷 新" class="refresh-btn" onclick="javascript: location.href = location.href;"></h3>
        <h3>文件目录管理器</h3>
    </div>

    <div class="upload-section">
        <form class="upload-form" method="post" action="/upload?path={{req_path}}" enctype="multipart/form-data" target="hidden_iframe" onsubmit="return addMessage();">
            <input type="file" name="file" class="file-input">
            <input type="submit" value="上传文件" class="upload-btn" onclick="showUploadingMessage();">
        </form>
        <div id="messages" class="messages"></div>
        <div id="uploadingMessage" class="uploading-message" style="display:none;">正在上传文件,请稍候...</div>
    </div>

    <div class="table-container">
        <div class="table-header">
            <h4>当前目录: {{the_path}}</h4>
        </div>
        <div class="table-scroll">
            <table>
                <thead>
                    <tr>
                        <th>文件名称</th>
                        <th>文件类别</th>
                        <th>文件大小</th>
                        <th>访问权限</th>
                        <th>所属用户</th>
                        <th>所属组</th>                
                        <th>最后修改时间</th>
                    </tr>
                </thead>
                <tbody>
                    {% for item in data %}
                    <tr class="{{ item.type }}">
                        <td><a href="{{ item.href }}" class="{{ item.type }}">{{ item.name }}</a></td>
                        <td>{{ item.type }}</td>
                        <td>{{ item.size }}</td>
                        <td>{{ item.permissions }}</td>
                        <td>{{ item.owner }}</td>
                        <td>{{ item.group }}</td>
                        <td>{{ item.mtime }}</td>
                    </tr>
                    {% endfor %}
                </tbody>
            </table>
        </div>
    </div>

    <div class="footer">
        <p>文件目录管理系统 | 优雅简洁的文件浏览体验</p>
    </div>

    <iframe name="hidden_iframe" id="hidden_iframe" style="display:none;"></iframe>
    
    <script type="text/javascript">
        function showUploadingMessage() {
            document.getElementById('uploadingMessage').style.display = 'block';
        }

        function addMessage() {
            var iframe = document.getElementById('hidden_iframe');
            iframe.onload = function() {
                var content = iframe.contentDocument || iframe.contentWindow.document;
                var message = content.body.innerHTML;
                var messagesDiv = document.getElementById('messages');
                messagesDiv.innerHTML = '<p>' + message + '</p>';
                messagesDiv.style.display = 'block';
                document.getElementById('uploadingMessage').style.display = 'none';
                
                // 3秒后隐藏消息
                setTimeout(function() {
                    messagesDiv.style.display = 'none';
					location.href = location.href;
                }, 3000);
            };
            return true;
        }
    </script>
</body>
</html>

新模板的页面效果如下:

🏷️ 如有疑问,可以关注 我的知识库,直接提问即可。