吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 5203|回复: 51
收起左侧

[Python 原创] 局域网文件传输神器电脑、手机无缝对接!

  [复制链接]
xueyi 发表于 2024-6-16 17:38
本帖最后由 xueyi 于 2024-6-16 17:43 编辑

【软件介绍】

工作需要跨设备文件传输的需求日益增长。为了Android、iPhone以及电脑设备之间的文件互传方便,我写了一款无需安装任意终端都可以使用的局域网文件传输工具。

【开发说明】

利用局域网技术,实现快速且稳定的文件传输,无需消耗移动数据。
电脑运行程序即自动搭建服务器,无需复杂的配置或设置。

【技术说明】

Flask:一个轻量级的Web框架,用于快速搭建内网中的HTTP服务器,提供文件上传和下载的Web接口。
socket:Python标准库中的网络通信库,用于处理底层网络通信,获取局域网IP地址。
qrcode:用于生成二维码的库,方便移动设备快速访问Web界面。
colorama:用于在终端或控制台应用程序中添加颜色和样式到文本输出

运行run.py同局域网的电脑访问直接访问http://[局域网地址]:5001

【效果图】


运行界面

运行界面

界面

界面

上传

上传





【代码说明】

run.py

from flask import Flask, request, send_from_directory, jsonify, abort, render_template
import os
import socket
import qrcode
app = Flask(__name__)
from colorama import init, Fore
# 初始化Colorama
init(autoreset=True)

# 设置文件存储的根目录
FILE_DIRECTORY = os.path.join(os.getcwd(), 'files')

@app.route('/')
def index():
    # 使用render_template渲染index.html
    return render_template('index.html')

# 设置文件上传和下载的路由
@app.route('/upload', methods=['POST'])
def upload_file():
    # 检查是否有文件在请求中
    if 'file' not in request.files:
        return jsonify({'error': '无文件'}), 400
    files = request.files.getlist('file')  # 获取所有上传的文件列表
    for file in files:
        if file.filename == '':
            return jsonify({'error': '没有选择文件'}), 400
        if file:
            filename = file.filename
             # 检查文件是否已存在,并添加序号
            counter = 1
            file_path = os.path.join(FILE_DIRECTORY, filename)
            while os.path.exists(file_path):
                file_name, file_ext = os.path.splitext(filename)
                new_filename = f"{file_name}_{counter}{file_ext}"
                file_path = os.path.join(FILE_DIRECTORY, new_filename)
                counter += 1
            file.save(os.path.join(FILE_DIRECTORY, file_path))
            return jsonify({'message': '文件上传成功', 'filename': filename}), 200
    return jsonify({'error': '上传文件时发生错误'}), 500

@app.route('/download/<filename>')
def download_file(filename):
     # 拼接完整的文件路径
    file_path = os.path.join(FILE_DIRECTORY, filename)
    # 检查文件是否存在
    if not os.path.isfile(file_path):
        print(f"文件不存在: {file_path}")  # 打印日志
        abort(404)  # 如果文件不存在,返回404错误
    return send_from_directory(FILE_DIRECTORY, filename, as_attachment=True)

@app.route('/list')
def list_files():
    # 获取请求的目录路径
    requested_path = request.args.get('path', FILE_DIRECTORY)
    full_path = os.path.join(FILE_DIRECTORY, requested_path)

    # 确保请求的路径是存在的目录
    if not os.path.isdir(full_path):
        return jsonify({'error': 'Directory not found'}), 404

    # 遍历目录
    files = []
    for item in os.listdir(full_path):
        item_path = os.path.join(full_path, item)
        if os.path.isfile(item_path):
            files.append(item)

    # 返回文件列表
    return jsonify(files)
'''
获取局域网IP地址
@return: 局域网IP地址
'''
def get_lan_ip():
    try:
        # 获取主机名
        hostname = socket.gethostname()
        # 获取与主机名关联的所有IP地址信息
        hostname_info = socket.gethostbyname_ex(hostname)
        # 获取所有IP地址列表
        ips = hostname_info[2]
        # 遍历IP地址列表,返回第一个非本地回环地址
        for ip in ips:
            if str(ip).startswith('192.'):
                return ip
        # 如果没有找到非回环地址,返回本地回环地址
        return '127.0.0.1'
    except socket.error as e:
        print(f"Error getting LAN IP: {e}")
        return '127.0.0.1'
'''
创建二维码
@Param url: 二维码链接
@param filename: 二维码文件名
'''
def create_qr_code(url, filename):
    qr = qrcode.QRCode(
        version=1,
        box_size=10,
        border=5
    )
    qr.add_data(url)
    qr.make(fit=True)
    img = qr.make_image(fill_color="black", back_color="white")
    img.save(filename)

'''
打印二维码到控制台
@param data: 二维码数据
@param fill_char: 填充字符
@param back_char: 背景字符
@param fill_color: 填充颜色
@param back_color: 背景颜色
'''
def print_qr_code_console(data, fill_char='█', back_char=' ',box_size=1, version=1):
    # 创建二维码对象
    qr = qrcode.QRCode(
        version=version,
        error_correction=qrcode.constants.ERROR_CORRECT_L,
        box_size=box_size,  # 设置单元格的大小
        border=1
    )
    qr.add_data(data)
    qr.make(fit=True)

    # 生成二维码图片
    qr_img = qr.make_image(fill_color="black", back_color="white")

    # 打印二维码
    for y in range(qr_img.size[1]):
        for x in range(qr_img.size[0]):
            # 根据二维码的黑白部分选择填充字符
            color = qr_img.getpixel((x, y))
            # 根据颜色值选择字符
            char = fill_char if color else back_char            # 使用ANSI颜色代码打印字符
            print(Fore.BLUE + char, end='')
        print()  # 每行结束后换行

if __name__ == '__main__':
     # 获取局域网IP地址
    local_ip = get_lan_ip()
    # 构建访问 Flask 应用的 URL
    flask_url = f"http://{local_ip}:5001"
    print(flask_url)
    # 定义二维码图片的文件名
    qr_code_filename = 'qr_code.png'
    # 生成并保存二维码图片
    create_qr_code(flask_url, qr_code_filename)
    print_qr_code_console(flask_url)
    app.run(host='0.0.0.0', port=5001)  # 监听所有可用的网络接口

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件传输页面</title>
    <script src="https://code.jquery.com/jquery-3.5.1.min.js"></script>
</head>

<body>
    <h1>文件列表</h1>
    <div id="file-list"></div>
    <h2>上传文件</h2>
    <input type="file" id="file-upload" multiple />
    <button>上传</button>
    <!-- 添加一个遮罩层元素 -->
    <div id="overlay" style="display:none;">
        <div class="overlay-content">
            <p>正在上传文件...</p>
            <div class="progress-bar-container">
                <div id="progress-bar" class="progress-bar"></div>
            </div>
        </div>
    </div>
    <script>
        function showOverlay() {
            document.getElementById('overlay').style.display = 'block';
        }

        function hideOverlay() {
            document.getElementById('overlay').style.display = 'none';
        }

        // 列出文件
        function listFiles() {
            $.ajax({
                url: '/list', // 服务器上的路由,用于获取文件列表
                type: 'GET',
                success: function (files) {
                    var fileList = files; // 假设服务器返回的是文件数组
                    var filesHtml = fileList.map(function (file) {
                        return '<div><a href="/download/' + encodeURIComponent(file) + '">' + file + '</a></div>';
                    }).join('');
                    $('#file-list').html(filesHtml);
                },
                error: function () {
                    alert('无法获取文件列表');
                }
            });
        }
        // 更新进度条
        function updateProgressBar(percentComplete, total) {
            var progressBarWidth = percentComplete / total * 100 + '%';
            console.log(progressBarWidth);
            document.getElementById('progress-bar').style.width = progressBarWidth;
        }
        // 上传文件
        function uploadFile() {
            showOverlay(); // 打开遮罩
            var fileInput = document.getElementById('file-upload');
            var files = fileInput.files;
            var totalFiles = files.length; // 总文件数
            var uploadCount = 0; // 设置上传计数器
            // 为每个文件上传创建单独的AJAX请求
            for (var i = 0; i < files.length; i++) {
                var formData = new FormData();
                formData.append('file', files[i]);
                $.ajax({
                    url: '/upload', // 服务器上的路由,用于上传文件
                    type: 'POST',
                    data: formData,
                    contentType: false,
                    processData: false,
                    success: function (data) {
                        console.log(data);
                    },
                    error: function (xhr) {
                        console.error(xhr.responseText);
                    },
                    complete: function () {
                        updateProgressBar(uploadCount, totalFiles);
                        // 所有文件上传完毕后执行的操作
                        if (++uploadCount === totalFiles) {
                            alert('所有文件上传成功');
                            listFiles(); // 刷新文件列表
                            hideOverlay(); // 关闭遮罩
                        }
                    }
                });
            }
        }

        // 页面加载时列出文件
        $(document).ready(function () {
            listFiles();
        });
    </script>

    <style>
        body {
            font-family: 'Arial', sans-serif;
            background: #f7f7f7;
            margin: 0;
            padding: 20px;
            color: #333;
        }

        #overlay {
            position: fixed;
            /* 固定定位,覆盖整个视口 */
            display: none;
            /* 默认不显示 */
            width: 100%;
            height: 100%;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background-color: rgba(0, 0, 0, 0.5);
            /* 黑色背景,半透明 */
            z-index: 2;
            /* 确保遮罩层在其他内容之上 */
        }

        .overlay-content {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            text-align: center;
        }

        h1,
        h2 {
            font-weight: normal;
            margin-top: 0;
        }

        #file-list {
            margin: 20px 0;
            padding: 10px;
            background: #fff;
            border-radius: 5px;
            box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
        }

        #file-list a {
            text-decoration: none;
            color: #337ab7;
        }

        button {
            background-color: #5cb85c;
            color: white;
            border: none;
            padding: 10px 20px;
            margin: 10px 0;
            border-radius: 5px;
            cursor: pointer;
            transition: background-color 0.3s ease;
        }

        button:hover {
            background-color: #4cae4c;
        }

        input[type="file"] {
            margin-bottom: 10px;
        }

        .progress-bar-container {
            width: 100%;
            background-color: #ddd;
            border-radius: 5px;
        }

        .progress-bar {
            height: 20px;
            background-color: #4caf50;
            border-radius: 5px;
            width: 0%;
            /* 初始进度为0% */
            transition: width 0.3s ease;
            /* 平滑过渡效果 */
        }

        /* 响应式设计 */
        @media (max-width: 768px) {
            body {
                padding: 10px;
            }

            .overlay-content {
                width: 90%;
            }
        }
    </style>
</body>

</html>

免费评分

参与人数 10吾爱币 +15 热心值 +9 收起 理由
huguo002 + 1 + 1 不错,有点意思!
yinfubuku + 1 + 1 用心讨论,共获提升!
t9lacky + 1 + 1 我很赞同!
junjia215 + 1 + 1 用心讨论,共获提升!
ase1993 + 1 + 1 用心讨论,共获提升!
lookfeiji + 1 + 1 谢谢@Thanks!
FitContent + 1 + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
PanHuang + 1 谢谢@Thanks!
sxhytds + 1 感谢发布原创作品,吾爱破解论坛因你更精彩!
wushaominkk + 7 + 1 欢迎分析讨论交流,吾爱破解论坛有你更精彩!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| xueyi 发表于 2024-6-17 01:13
黑白夜丶 发表于 2024-6-17 01:09
苹果手机可以用吗?最大可以传输多大的文件呢。

支持,Flask 默认支持 10M
如果需要大文件建议在 app.py 中修改
[Python] 纯文本查看 复制代码
# 设置最大上传文件大小为 20MB
app.config['MAX_CONTENT_LENGTH'] = 20 * 1024 * 1024
lsgd002 发表于 2024-6-17 09:10
hk9186 发表于 2024-6-17 02:12
只能支持20M以下传输吗?一个apk文件都有100M了
黑白夜丶 发表于 2024-6-17 01:09
苹果手机可以用吗?最大可以传输多大的文件呢。
红蓝黄 发表于 2024-6-17 05:34
这类软件太多了吧
shojnhv 发表于 2024-6-17 06:46
谢谢,虽然这类软件很多,主要是学习相关编程技术来的
龍謹 发表于 2024-6-17 08:06
正在学习PY,偶来研究研究。
提拉米苏子冉 发表于 2024-6-17 08:09
跨网段局域网能传输吗
dhsfb 发表于 2024-6-17 08:37
偶也复制弄一个
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2024-12-12 19:33

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表