局域网文件传输神器电脑、手机无缝对接!
本帖最后由 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
# 遍历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):
for x in range(qr_img.size):
# 根据二维码的黑白部分选择填充字符
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);
$.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 {
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>
``` 黑白夜丶 发表于 2024-6-17 01:09
苹果手机可以用吗?最大可以传输多大的文件呢。
支持,Flask 默认支持 10M
如果需要大文件建议在 app.py 中修改
# 设置最大上传文件大小为 20MB
app.config['MAX_CONTENT_LENGTH'] = 20 * 1024 * 1024 正在学习PY,偶来研究研究。
只能支持20M以下传输吗?一个apk文件都有100M了 苹果手机可以用吗?最大可以传输多大的文件呢。 这类软件太多了吧 谢谢,虽然这类软件很多,主要是学习相关编程技术来的 正在学习PY,偶来研究研究。 跨网段局域网能传输吗 偶也复制弄一个