工作需要跨设备文件传输的需求日益增长。为了Android、iPhone以及电脑设备之间的文件互传方便,我写了一款无需安装任意终端都可以使用的局域网文件传输工具。
【开发说明】
利用局域网技术,实现快速且稳定的文件传输,无需消耗移动数据。
电脑运行程序即自动搭建服务器,无需复杂的配置或设置。
【技术说明】
Flask:一个轻量级的Web框架,用于快速搭建内网中的HTTP服务器,提供文件上传和下载的Web接口。
socket:Python标准库中的网络通信库,用于处理底层网络通信,获取局域网IP地址。
qrcode:用于生成二维码的库,方便移动设备快速访问Web界面。
colorama:用于在终端或控制台应用程序中添加颜色和样式到文本输出
运行run.py同局域网的电脑访问直接访问http://[局域网地址]:5001
【效果图】
WechatIMG521.jpg (56.85 KB, 下载次数: 0)
下载附件
运行界面
2024-6-16 17:42 上传
IMG_2399.png (82.01 KB, 下载次数: 0)
下载附件
界面
2024-6-16 17:42 上传
IMG_2400.png (86.85 KB, 下载次数: 0)
下载附件
上传
2024-6-16 17:42 上传
【代码说明】
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/')
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
文件传输页面
文件列表
上传文件
上传
正在上传文件...
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%;
}
}