1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| from flask import Flask, render_template, send_file, abort, request from pathlib import Path from itertools import islice import os
app = Flask(__name__) BASE_DIR = Path(r"/home/alin/Desktop/myproject/books/")
def scan_books(path): """递归扫描目录结构""" structure = { "name": path.name, "type": "directory", "children": [], "path": str(path.relative_to(BASE_DIR)) } for item in path.iterdir(): if item.is_dir(): structure["children"].append(scan_books(item)) elif item.suffix == ".txt": structure["children"].append({ "name": item.stem, "type": "file", "path": str(item.relative_to(BASE_DIR)) }) return structure
def validate_path(relative_path): """防止路径穿越攻击""" target = (BASE_DIR / relative_path).resolve() try: target.relative_to(BASE_DIR.resolve()) except ValueError: abort(404) return target
@app.route("/") def index(): return render_template("index.html", tree=scan_books(BASE_DIR), current_path="")
@app.route("/browse/<path:subpath>") def browse(subpath): target = validate_path(subpath) if not target.is_dir(): abort(404) breadcrumbs = [("首页", "")] parts = subpath.split('/') for i in range(len(parts)): breadcrumbs.append((parts[i], '/'.join(parts[:i+1]))) return render_template("index.html", tree=scan_books(target), current_path=subpath, breadcrumbs=breadcrumbs)
@app.route("/read/<path:filename>") def read_book(filename): filepath = validate_path(filename) if not filepath.is_file(): abort(404) page = request.args.get('page', 1, type=int) per_page = 1000 with open(filepath, 'r', encoding='utf-8') as f: lines = list(islice(f, (page-1)*per_page, page*per_page)) return render_template("reader.html", title=filepath.stem, content=''.join(lines), page=page, total_pages=(os.path.getsize(filepath)//(per_page*50)+1))
|