ubuntu nginx文件服务器

bash 复制代码
#!/bin/bash

# 安装Nginx
sudo apt update
sudo apt install nginx -y

# 创建APK存储目录
sudo mkdir -p /var/www/apk-repo/files
sudo chown -R www-data:www-data /var/www/apk-repo

# 配置Nginx站点
sudo tee /etc/nginx/sites-available/apk-repo > /dev/null <<EOF
server {
    listen 80;
    server_name _;
    
    root /var/www/apk-repo;
    index index.html;
    
    # 启用目录浏览
    autoindex on;
    autoindex_exact_size off;
    autoindex_localtime on;
    
    # 文件上传配置
    client_max_body_size 100M;
    
    location /files/ {
        # 允许上传和下载
        dav_methods PUT DELETE MKCOL COPY MOVE;
        dav_ext_methods PROPFIND OPTIONS;
        create_full_put_path on;
        
        # 上传权限
        limit_except GET {
            allow all;
        }
    }
    
    # 上传处理
    location /upload {
        # 处理文件上传
        client_body_temp_path /var/www/apk-repo/temp;
        dav_access user:rw group:rw all:r;
    }
}
EOF

# 启用站点
sudo ln -sf /etc/nginx/sites-available/apk-repo /etc/nginx/sites-enabled/
sudo rm -f /etc/nginx/sites-enabled/default

# 重启Nginx
sudo systemctl enable nginx
sudo systemctl restart nginx

echo "APK文件服务器已部署完成"
echo "访问地址: http://你的服务器IP"
echo "APK文件存储路径: /var/www/apk-repo/files"

nginx-setup.sh

index.html

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>APK文件管理服务器</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css">
    <style>
        .file-card:hover {
            transform: translateY(-2px);
            transition: all 0.3s ease;
        }
        .progress-bar {
            transition: width 0.3s ease;
        }
    </style>
</head>
<body class="bg-gray-50 min-h-screen">
    <!-- 导航栏 -->
    <nav class="bg-blue-600 text-white shadow-lg">
        <div class="container mx-auto px-4 py-3">
            <div class="flex justify-between items-center">
                <h1 class="text-2xl font-bold">
                    <i class="fas fa-mobile-alt mr-2"></i>
                    APK文件管理服务器
                </h1>
                <div class="text-sm">
                    <span id="current-time"></span>
                </div>
            </div>
        </div>
    </nav>

    <!-- 主内容区 -->
    <div class="container mx-auto px-4 py-8">
        <!-- 上传区域 -->
        <div class="bg-white rounded-lg shadow-md p-6 mb-8">
            <h2 class="text-xl font-semibold mb-4 text-gray-800">
                <i class="fas fa-upload mr-2"></i>上传APK文件
            </h2>
            <div class="border-2 border-dashed border-gray-300 rounded-lg p-8 text-center hover:border-blue-400 transition-colors">
                <input type="file" id="file-input" accept=".apk" multiple 
                       class="hidden">
                <div class="flex flex-col items-center justify-center space-y-4">
                    <i class="fas fa-cloud-upload-alt text-4xl text-gray-400"></i>
                    <p class="text-gray-600">点击选择或拖拽APK文件到此处</p>
                    <button onclick="document.getElementById('file-input').click()" 
                            class="bg-blue-500 hover:bg-blue-600 text-white px-6 py-2 rounded-lg transition-colors">
                        选择文件
                    </button>
                </div>
                <div id="upload-progress" class="mt-4 hidden">
                    <div class="text-sm text-gray-600 mb-2">上传进度</div>
                    <div class="bg-gray-200 rounded-full h-2">
                        <div id="progress-bar" class="bg-green-500 h-2 rounded-full progress-bar" style="width: 0%"></div>
                    </div>
                    <div id="progress-text" class="text-sm text-gray-600 mt-1">0%</div>
                </div>
            </div>
        </div>

        <!-- 文件列表 -->
        <div class="bg-white rounded-lg shadow-md p-6">
            <div class="flex justify-between items-center mb-4">
                <h2 class="text-xl font-semibold text-gray-800">
                    <i class="fas fa-list mr-2"></i>APK文件列表
                </h2>
                <button onclick="refreshFileList()" 
                        class="bg-gray-500 hover:bg-gray-600 text-white px-4 py-2 rounded-lg transition-colors">
                    <i class="fas fa-sync-alt mr-2"></i>刷新
                </button>
            </div>
            <div id="file-list" class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
                <div class="text-center text-gray-500 py-8">
                    <i class="fas fa-spinner fa-spin text-2xl mb-2"></i>
                    <p>加载中...</p>
                </div>
            </div>
        </div>
    </div>

    <!-- 页脚 -->
    <footer class="bg-gray-800 text-white py-6 mt-12">
        <div class="container mx-auto px-4 text-center">
            <p>&copy; 2025 APK文件管理服务器 - 基于Nginx构建</p>
        </div>
    </footer>

    <script>
        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            updateTime();
            setInterval(updateTime, 1000);
            loadFileList();
            setupDragAndDrop();
        });

        // 更新时间
        function updateTime() {
            const now = new Date();
            document.getElementById('current-time').textContent = 
                now.toLocaleString('zh-CN');
        }

        // 设置拖拽上传
        function setupDragAndDrop() {
            const dropArea = document.querySelector('.border-dashed');
            
            ['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
                dropArea.addEventListener(eventName, preventDefaults, false);
            });
            
            function preventDefaults(e) {
                e.preventDefault();
                e.stopPropagation();
            }
            
            ['dragenter', 'dragover'].forEach(eventName => {
                dropArea.addEventListener(eventName, highlight, false);
            });
            
            ['dragleave', 'drop'].forEach(eventName => {
                dropArea.addEventListener(eventName, unhighlight, false);
            });
            
            dropArea.addEventListener('drop', handleDrop, false);
            
            function highlight() {
                dropArea.classList.add('border-blue-400', 'bg-blue-50');
            }
            
            function unhighlight() {
                dropArea.classList.remove('border-blue-400', 'bg-blue-50');
            }
            
            function handleDrop(e) {
                const dt = e.dataTransfer;
                const files = dt.files;
                handleFiles(files);
            }
        }

        // 文件输入处理
        document.getElementById('file-input').addEventListener('change', function(e) {
            handleFiles(e.target.files);
        });

        // 处理文件上传
        function handleFiles(files) {
            const apkFiles = Array.from(files).filter(file => 
                file.name.toLowerCase().endsWith('.apk')
            );
            
            if (apkFiles.length === 0) {
                alert('请选择APK文件!');
                return;
            }
            
            uploadFiles(apkFiles);
        }

        // 上传文件
        function uploadFiles(files) {
            const progressDiv = document.getElementById('upload-progress');
            const progressBar = document.getElementById('progress-bar');
            const progressText = document.getElementById('progress-text');
            
            progressDiv.classList.remove('hidden');
            
            let uploadedCount = 0;
            const totalFiles = files.length;
            
            files.forEach((file, index) => {
                const formData = new FormData();
                formData.append('file', file);
                
                const xhr = new XMLHttpRequest();
                
                xhr.upload.addEventListener('progress', function(e) {
                    if (e.lengthComputable) {
                        const percentComplete = (e.loaded / e.total) * 100;
                        progressBar.style.width = percentComplete + '%';
                        progressText.textContent = 
                            `上传 ${file.name}: ${Math.round(percentComplete)}%`;
                    }
                });
                
                xhr.addEventListener('load', function() {
                    uploadedCount++;
                    if (uploadedCount === totalFiles) {
                        setTimeout(() => {
                            progressDiv.classList.add('hidden');
                            progressBar.style.width = '0%';
                            loadFileList();
                            alert('文件上传完成!');
                        }, 1000);
                    }
                });
                
                xhr.open('PUT', `/files/${file.name}`);
                xhr.send(formData);
            });
        }

        // 加载文件列表
        async function loadFileList() {
            try {
                const response = await fetch('/files/');
                const text = await response.text();
                parseFileList(text);
            } catch (error) {
                console.error('加载文件列表失败:', error);
                document.getElementById('file-list').innerHTML = 
                    '<div class="text-center text-red-500 py-8">加载失败,请刷新页面</div>';
            }
        }

        // 解析文件列表
        function parseFileList(html) {
            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            const links = doc.querySelectorAll('a[href]');
            
            const fileList = Array.from(links)
                .filter(link => link.href.endsWith('.apk'))
                .map(link => {
                    const href = link.getAttribute('href');
                    const text = link.textContent.trim();
                    const sizeMatch = text.match(/(\d+)\s*(\w+)/);
                    
                    return {
                        name: href,
                        displayName: href.split('/').pop(),
                        size: sizeMatch ? sizeMatch[0] : '未知大小'
                    };
                });
            
            displayFileList(fileList);
        }

        // 显示文件列表
        function displayFileList(files) {
            const fileListDiv = document.getElementById('file-list');
            
            if (files.length === 0) {
                fileListDiv.innerHTML = `
                    <div class="col-span-3 text-center text-gray-500 py-8">
                        <i class="fas fa-inbox text-4xl mb-2"></i>
                        <p>暂无APK文件</p>
                    </div>
                `;
                return;
            }
            
            fileListDiv.innerHTML = files.map(file => `
                <div class="file-card bg-white border border-gray-200 rounded-lg p-4 shadow-sm hover:shadow-md transition-all">
                    <div class="flex items-center justify-between mb-2">
                        <i class="fas fa-android text-green-500 text-xl"></i>
                        <span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded">
                        APK
                        </span>
                    </div>
                    <h3 class="font-medium text-gray-800 truncate mb-1" title="${file.displayName}">
                        ${file.displayName}
                    </h3>
                    <p class="text-sm text-gray-600 mb-3">${file.size}</p>
                    <div class="flex space-x-2">
                        <button onclick="downloadFile('${file.name}')" 
                                class="flex-1 bg-green-500 hover:bg-green-600 text-white py-2 px-3 rounded text-sm transition-colors">
                            <i class="fas fa-download mr-1">
                            下载
                        </button>
                        <button onclick="deleteFile('${file.name}')" 
                                class="bg-red-500 hover:bg-red-600 text-white py-2 px-3 rounded text-sm transition-colors">
                            <i class="fas fa-trash"></i>
                        </button>
                    </div>
                </div>
            `).join('');
        }

        // 下载文件
        function downloadFile(filename) {
            window.open(filename, '_blank');
        }

        // 删除文件
        async function deleteFile(filename) {
            if (!confirm(`确定要删除 ${filename} 吗?`)) {
                return;
            }
            
            try {
                const response = await fetch(filename, {
                    method: 'DELETE'
                });
                
                if (response.ok) {
                    loadFileList();
                    alert('文件删除成功!');
                } else {
                    alert('文件删除失败!');
                }
            } catch (error) {
                console.error('删除文件失败:', error);
                alert('删除失败,请检查服务器配置');
            }
        }

        // 刷新文件列表
        function refreshFileList() {
            loadFileList();
        }
    </script>
</body>
</html>

requirement.txt

bash 复制代码
# Web服务器依赖
nginx>=1.18
# 文件管理界面依赖(前端)
tailwindcss>=3.0
font-awesome>=6.0
相关推荐
kblj55551 小时前
学习Linux——学习工具——DNS--BIND工具
linux·运维·学习
晚风吹长发1 小时前
初步了解Linux中文件描述符-fd
linux·运维·服务器·c++·开发·文件
微风◝1 小时前
AlmaLinux9配置本地镜像仓库
linux·运维·服务器
赖small强1 小时前
【Linux C/C++开发】Linux C/C++ 高效延迟崩溃分析:基于 mprotect 的内存陷阱技术 (Electric Fence)
linux·c语言·c++·mprotect·buffer overflow
保持低旋律节奏1 小时前
linux——make/Makefile自动化工程构建
linux·运维·自动化
云计算练习生1 小时前
渗透测试行业术语—— 网络攻击方式与漏洞利用
服务器·网络·安全·渗透测试术语·网络安全术语
繁华似锦respect1 小时前
C++ & Linux 中 GDB 调试与内存泄漏检测详解
linux·c语言·开发语言·c++·windows·算法
爱潜水的小L1 小时前
自学嵌入式day25,树
linux
周杰伦_Jay1 小时前
【Linux Shell】命令完全指南
linux·运维·服务器