Эх сурвалжийг харах

Merge branch 'xuxinyi' of web/FlaskProject into master

xuxinyi 9 сар өмнө
parent
commit
189d6a1d40

+ 2 - 2
.idea/sqldialects.xml

@@ -1,7 +1,7 @@
 <?xml version="1.0" encoding="UTF-8"?>
 <project version="4">
   <component name="SqlDialectMappings">
-    <file url="file://$PROJECT_DIR$/data.py" dialect="GenericSQL" />
-    <file url="file://$PROJECT_DIR$/files.py" dialect="GenericSQL" />
+    <file url="file://$PROJECT_DIR$/db_utils/data.py" dialect="GenericSQL" />
+    <file url="file://$PROJECT_DIR$/db_utils/files.py" dialect="GenericSQL" />
   </component>
 </project>

+ 45 - 10
app.py

@@ -1,23 +1,55 @@
 # -*- coding: utf-8 -*-
-from flask import Flask, jsonify, request
-from data import *
-from files import *
+from flask import Flask, jsonify, request, render_template
+from db_utils.data import *
+from db_utils.files import *
 from file_uploader import upload_file  # 导入文件上传的函数
 
 # 初始化 Flask 应用
 app = Flask(__name__)
 
 # 初始化数据库和表
-initialize_database()
-initialize_table()
+#initialize_database()
+#initialize_table()
 
 # 初始化数据库和表
-initialize_files_database()
-initialize_files_table()
+#initialize_files_database()
+#initialize_files_table("filePath")
 
 @app.route('/')
 def home():
-    return "Welcome to the Fruit API!"
+    return render_template('index.html')
+# 获取类别数据
+@app.route('/categories')
+def get_categories():
+    rows = fetch_data('category')
+
+    # 将查询结果格式化为字典列表
+    category_list = [{"id": row[0], "categoryName": row[1]} for row in rows]
+    print(category_list)
+    # 返回 JSON 格式的数据
+    return jsonify(category_list)
+
+@app.route('/category-data', methods=['GET'])
+def get_table():
+    # 获取查询参数中的表名 (categoryName)
+    category_name = request.args.get('categoryName')
+
+    if not category_name:
+        return jsonify({"error": "No categoryName provided"}), 400  # 如果没有提供表名,返回错误
+
+    # 根据表名查询数据
+    rows = fetch_data(category_name)
+
+    if not rows:
+        return jsonify({"error": f"No data found for table {category_name}"}), 404  # 如果没有找到数据,返回错误
+
+    # 将查询结果格式化为字典列表
+    category_list = [{"id": row[0], "categoryName": row[1], "url":row[2]} for row in rows]
+    print(category_list)
+
+    # 返回 JSON 格式的数据
+    return jsonify(category_list)
+
 
 # 添加水果数据
 @app.route('/add_fruit', methods=['POST'])
@@ -66,10 +98,13 @@ def upload():
     return upload_file()  # 调用封装好的文件上传函数
 
 # 下载文件接口
+from flask import send_from_directory
+
 @app.route('/download/<filename>', methods=['GET'])
 def download_file(filename):
-    from flask import send_from_directory
-    return send_from_directory('uploads', filename)
+    # 强制下载文件
+    return send_from_directory('../uploads', filename, as_attachment=True)
+
 
 # 运行 Flask 应用
 if __name__ == '__main__':

+ 0 - 0
db_utils/__init__.py


+ 1 - 1
data.py → db_utils/data.py

@@ -8,7 +8,7 @@ DB_CONFIG = {
     'user': 'root',
     'password': '1',
     'port': 6003,
-    'database': 'fruit'  # 替换为数据库名称
+    'database': 'files'  # 替换为数据库名称
 }
 
 # 初始化数据库

+ 59 - 64
files.py → db_utils/files.py

@@ -1,4 +1,6 @@
 # -*- coding: utf-8 -*-
+from unicodedata import category
+
 import mysql.connector
 from mysql.connector import Error
 
@@ -45,31 +47,23 @@ def initialize_files_database():
             connection.close()
             print("MySQL 服务器连接已关闭")
 
-# 初始化表
-def initialize_files_table(table_name=None):
-    if not table_name:
-        table_name = 'filePath'
-
-    # 验证表名是否合法(这里的规则可以根据你的需求做调整)
-    allowed_tables = ['fruit', 'filePath']  # 你可以在这里列出允许的表名
-    if table_name not in allowed_tables:
-        raise ValueError(f"不允许使用此表名:{table_name}")
-
+def initialize_files_table(table_name = "category"):
     try:
         connection = mysql.connector.connect(**DB_CONFIG)
         if connection.is_connected():
             cursor = connection.cursor()
 
-            # 动态创建表
+            # 动态创建表,指定字符集为 utf8mb4
             create_table_query = f'''
                 CREATE TABLE IF NOT EXISTS `{table_name}` (
                     id INT AUTO_INCREMENT PRIMARY KEY,
-                    name VARCHAR(100)
-                )
+                    name VARCHAR(255) UNIQUE,
+                    url VARCHAR(255) UNIQUE
+                ) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci
             '''
 
             cursor.execute(create_table_query)
-            print(f"--- 创建 {table_name} 表 ---")
+            print(f"表 {table_name} 初始化成功。")
     except Error as err:
         print(f"初始化表时发生错误:{err}")
     finally:
@@ -78,56 +72,58 @@ def initialize_files_table(table_name=None):
             connection.close()
             print("MySQL 连接已关闭")
 
+
 # 插入数据
-def insert_url(name):
+def insert_url(name, url, table_name="category"):
     try:
         connection = mysql.connector.connect(**DB_CONFIG)
         if connection.is_connected():
             cursor = connection.cursor()
 
-            # 插入数据
-            cursor.execute("INSERT INTO filePath (name) VALUES (%s)", (name,))
+            # 确保目标文件表存在
+            initialize_files_table(table_name)
+
+            # 插入文件数据到目标表
+            insert_query = f"INSERT INTO `{table_name}` (name, url) VALUES (%s, %s)"
+            cursor.execute(insert_query, (name, url))
             connection.commit()
-            print(f"插入数据成功:({name})")
-    except Error as err:
-        print(f"插入数据时发生错误:{err}")
-    finally:
-        if connection.is_connected():
-            cursor.close()
-            connection.close()
-            print("MySQL 连接已关闭")
 
-# 插入数据
-def insert_data(name, origin):
-    try:
-        connection = mysql.connector.connect(**DB_CONFIG)
-        if connection.is_connected():
-            cursor = connection.cursor()
+            # 判断插入是否成功
+            if cursor.rowcount > 0:
+                print(f"插入文件数据成功:({name}, {url}) 到表 {table_name}")
+                file_result = {"status": "success"}
+            else:
+                print(f"插入文件数据失败:({name}, {url}) 到表 {table_name}")
+                file_result = {"status": "failed"}
 
-            # 插入数据
-            cursor.execute("INSERT INTO filePath (name, origin) VALUES (%s, %s)", (name, origin))
-            connection.commit()
-            print(f"插入数据成功:({name}, {origin})")
-    except Error as err:
-        print(f"插入数据时发生错误:{err}")
-    finally:
-        if connection.is_connected():
-            cursor.close()
-            connection.close()
-            print("MySQL 连接已关闭")
+            # 确保 category 表存在
+            initialize_files_table("category")
 
-def delete_data(name):
-    try:
-        connection = mysql.connector.connect(**DB_CONFIG)
-        if connection.is_connected():
-            cursor = connection.cursor()
+            # 查询是否已经存在该表名
+            select_query = "SELECT * FROM `category` WHERE `name` = %s"
+            cursor.execute(select_query, (table_name,))
+            existing_category = cursor.fetchone()
+
+            if existing_category:
+                print(f"表名 {table_name} 已经存在,不插入!")
+                category_result = {"status": "exists"}
+            else:
+                # 执行插入 category
+                insert_query = "INSERT INTO `category` (name) VALUES (%s)"
+                cursor.execute(insert_query, (table_name,))
+                connection.commit()
+                print(f"插入表名成功:({table_name}) 到表 category")
+                category_result = {"status": "success"}
+
+            return {"status": "success", "file_result": file_result, "category_result": category_result}
+
+    except mysql.connector.Error as err:
+        if err.errno == 1062:  # Duplicate entry error
+            print(f"文件 {name} 已存在,不重复插入。")
+            return {"status": "duplicate", "message": f"文件 {name} 已存在"}
+        print(f"插入数据时发生错误:{err}")
+        return {"status": "error", "message": str(err)}
 
-            # 删除数据
-            cursor.execute("DELETE FROM filePath WHERE name = %s", (name,))
-            connection.commit()
-            print(f"成功删除名称为 {name} 的记录")
-    except Error as err:
-        print(f"删除数据时发生错误:{err}")
     finally:
         if connection.is_connected():
             cursor.close()
@@ -135,17 +131,19 @@ def delete_data(name):
             print("MySQL 连接已关闭")
 
 
+
 # 查询数据
-def fetch_data():
+def fetch_data(table_name):
     try:
         connection = mysql.connector.connect(**DB_CONFIG)
         if connection.is_connected():
             cursor = connection.cursor()
 
             # 查询数据
-            cursor.execute("SELECT * FROM filePath")
+            query = f"SELECT * FROM `{table_name}`"
+            cursor.execute(query)
             rows = cursor.fetchall()
-            print("--- 查询结果 ---")
+            print(f"--- 表 {table_name} 的查询结果 ---")
             for row in rows:
                 print(row)
             return rows
@@ -159,13 +157,10 @@ def fetch_data():
 
 # 主函数
 if __name__ == "__main__":
-    # 初始化数据库和表
+    # 初始化数据库
     initialize_files_database()
-    initialize_files_table()
-
-    # 插入示例数据
-    insert_data('Apple', 'China')
-    insert_data('Banana', 'Ecuador')
 
-    # 查询数据
-    fetch_data()
+    # 示例:插入和查询数据
+    table_name = "filePath"
+    insert_url("example_file.mp4", table_name)
+    fetch_data(table_name)

+ 75 - 27
file_uploader.py

@@ -1,16 +1,27 @@
-# file_uploader.py
 import os
 from flask import request, jsonify
-
-from files import insert_url
+from werkzeug.utils import secure_filename
+from db_utils.files import *
 
 # 上传目录
-UPLOAD_FOLDER = 'uploads'
+UPLOAD_FOLDER = '../uploads'
 if not os.path.exists(UPLOAD_FOLDER):
     os.makedirs(UPLOAD_FOLDER)
 
 # 允许的文件扩展名
-ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf','mp4'}
+ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif', 'pdf', 'mp4', 'apk'}
+
+# 扩展名与数据库表的映射
+TABLE_MAPPING = {
+    'apk': 'apk_table',
+    'mp4': 'video_table',
+    'mp3': 'audio_table',
+    'pdf': 'document_table',
+    'jpg': 'image_table',
+    'png': 'image_table',
+    'jpeg': 'image_table',
+    'gif': 'image_table'
+}
 
 
 def allowed_file(filename):
@@ -20,33 +31,70 @@ def allowed_file(filename):
 
 def upload_file():
     """处理文件上传"""
-    # 检查请求中是否有文件
-    if 'file' not in request.files:
-        return jsonify({'error': 'No file part'}), 400
+    try:
+        # 检查请求中是否有文件
+        if 'file' not in request.files:
+            return jsonify({'error': 'No file part in request'}), 400
+
+        file = request.files['file']
+
+        # 检查文件是否为空
+        if file.filename == '':
+            return jsonify({'error': 'No selected file'}), 400
+
+        # 检查文件是否符合要求
+        if file and allowed_file(file.filename):
+            # 获取文件扩展名
+            file_ext = file.filename.rsplit('.', 1)[1].lower()
+
+            # 获取目标数据库表
+            target_table = TABLE_MAPPING.get(file_ext)
+            if not target_table:
+                return jsonify({'error': f'No table mapping for file type: {file_ext}'}), 400
+
+            # 保存文件到服务器
+            #filename = secure_filename(file.filename)  #格式化文件名
+            filename = file.filename
+            file_path = os.path.join(UPLOAD_FOLDER, filename)
+            file.save(file_path)
+
+            # 生成下载链接
+            download_url = f'http://127.0.0.1:5000/download/{filename}'
 
-    file = request.files['file']
+            # 插入数据库
+            try:
+                result = insert_url(filename, download_url, target_table)
+                print("result:", result)  # 调试输出
 
-    # 检查文件是否为空
-    if file.filename == '':
-        return jsonify({'error': 'No selected file'}), 400
+                if result.get("status") == "error":
+                    print("发生数据库错误:", result.get("message"))
+                    return jsonify({"error": f"Database error: {result.get('message')}"}), 500
+                else:
+                    # 文件插入结果
+                    if result.get("status") == "success":
+                        print("文件插入成功!")
+                    elif result.get("status") == "duplicate":
+                        print(f"文件 {filename} 已存在,不重复插入。")
+                        return jsonify({"message": f"文件 {filename} 已经存在"}), 409
+                    else:
+                        print("文件插入失败,返回结果:", result.get("file_result"))
 
-    # 检查文件是否符合要求
-    if file and allowed_file(file.filename):
-        # 保存文件到服务器
-        filename = os.path.join(UPLOAD_FOLDER, file.filename)
-        file.save(filename)
+                    # 分类表插入结果
+                    if result.get("category_result") and result["category_result"].get("status") == "exists":
+                        print("表名已存在,无需插入。")
+                    elif result.get("category_result") and result["category_result"].get("status") == "success":
+                        print("表名插入成功!")
+                    else:
+                        print("分类表插入失败,返回结果:", result.get("category_result"))
 
-        # 返回文件的下载链接
-        download_url = f'http://127.0.0.1:5000/download/{file.filename}'
+            except Exception as e:
+                print("异常信息:", str(e))
+                return jsonify({"error": f"Database error: {str(e)}"}), 500
 
-        if not download_url:
-            return jsonify({"error": "Missing name or origin"}), 400
+            return jsonify({'message': 'File uploaded successfully', 'download_url': download_url}), 200
 
-        try:
-            insert_url(download_url)
-        except Exception as e:
-            return jsonify({"error": str(e)}), 500
+        return jsonify({'error': 'File type not allowed'}), 400
 
-        return jsonify({'message': 'File uploaded successfully', 'download_url': download_url}), 200
+    except Exception as e:
+        return jsonify({"error": f"Server error: {str(e)}"}), 500
 
-    return jsonify({'error': 'File type not allowed'}), 400

+ 2 - 1
requirements.txt

@@ -1,2 +1,3 @@
 flask~=3.0.3
-mysql-connector-python~=9.0.0
+mysql-connector-python~=9.0.0
+werkzeug~=3.0.6

BIN
static/123.jpg


+ 232 - 0
templates/index.html

@@ -0,0 +1,232 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+    <meta charset="UTF-8">
+    <meta name="viewport" content="width=device-width, initial-scale=1.0">
+    <title>文件管理系统</title>
+    <script>
+        // Fetch categories from the backend
+        async function fetchCategories() {
+            const response = await fetch('/categories'); // Backend endpoint to get categories
+            const categories = await response.json();
+            const categoryList = document.getElementById('category-list');
+
+            // Populate the left-side list with categories
+            categoryList.innerHTML = '';
+            categories.forEach(category => {
+                const listItem = document.createElement('li');
+                listItem.textContent = category.categoryName;
+                listItem.onclick = () => fetchTableData(category.categoryName);
+                categoryList.appendChild(listItem);
+            });
+        }
+
+        async function fetchTableData(tableName) {
+            const response = await fetch(`/category-data?categoryName=${tableName}`); // Backend endpoint for table data
+            const tableData = await response.json();
+            const tableBody = document.getElementById('table-body');
+
+            // Populate the right-side table with data
+            tableBody.innerHTML = '';
+            tableData.forEach(row => {
+                const tableRow = document.createElement('tr');
+                // 显示 id 和 categoryName
+                const cells = [row.id, row.categoryName];
+                cells.forEach(value => {
+                    const cell = document.createElement('td');
+                    cell.textContent = value;
+                    tableRow.appendChild(cell);
+                });
+
+                // Add the download button in the last column
+                const downloadCell = document.createElement('td');
+                const downloadButton = document.createElement('button');
+
+                downloadButton.textContent = 'Download';
+                downloadButton.onclick = () => downloadFile(row.categoryName); // Assuming row[1] is the filename
+                downloadCell.appendChild(downloadButton);
+                tableRow.appendChild(downloadCell);
+
+                tableBody.appendChild(tableRow);
+            });
+        }
+
+        async function downloadFile(filename) {
+            const response = await fetch(`/download/${filename}`, {
+                method: 'GET',
+                headers: {
+                    'Content-Type': 'application/json'
+                }
+            });
+
+            if (response.ok) {
+                // Create a blob from the response
+                const blob = await response.blob();
+
+                // Create a link element
+                const link = document.createElement('a');
+                link.href = URL.createObjectURL(blob);
+                link.download = filename;
+
+                // Append the link to the body (it needs to be in the DOM to work)
+                document.body.appendChild(link);
+
+                // Programmatically trigger the download
+                link.click();
+
+                // Clean up the DOM
+                document.body.removeChild(link);
+            } else {
+                console.error('Failed to download the file');
+            }
+        }
+
+        // Function to handle file upload
+        async function uploadFile() {
+            const fileInput = document.getElementById('file-input');
+            const file = fileInput.files[0]; // Get the selected file
+            const formData = new FormData();
+            formData.append('file', file);
+
+            const response = await fetch('/upload', {
+                method: 'POST',
+                body: formData,
+            });
+
+            if (response.ok) {
+                alert('File uploaded successfully!');
+                fetchCategories(); // Reload categories after upload
+            } else if (response.status === 409) {
+                // 处理文件已存在的情况
+                const data = await response.json();
+                alert(data.message); // 显示 "File already exists, no need to insert again."
+            } else {
+                alert('Failed to upload file');
+            }
+        }
+
+        // Initial load
+        document.addEventListener('DOMContentLoaded', fetchCategories);
+    </script>
+    <style>
+        body {
+            font-family: Arial, sans-serif;
+            margin: 0;
+            display: flex;
+        }
+
+        #sidebar {
+            width: 20%;
+            background-color: #f8f9fa;
+            padding: 1em;
+            border-right: 1px solid #ddd;
+            display: flex;
+            flex-direction: column;
+            justify-content: flex-start;
+            height: 100vh;
+        }
+
+        #content {
+            flex-grow: 1;
+            padding: 1em;
+        }
+
+        ul {
+            list-style: none;
+            padding: 0;
+        }
+
+        li {
+            padding: 0.5em;
+            cursor: pointer;
+            border: 1px solid #ddd;
+            margin-bottom: 0.5em;
+            border-radius: 4px;
+            text-align: center;
+        }
+
+        li:hover {
+            background-color: #e9ecef;
+        }
+
+        table {
+            width: 100%;
+            border-collapse: collapse;
+        }
+
+        table, th, td {
+            border: 1px solid #ddd;
+        }
+
+        th, td {
+            padding: 0.5em;
+            text-align: left;
+        }
+
+        th {
+            background-color: #f1f1f1;
+        }
+
+        /* Styling the upload section to be at the bottom of the sidebar */
+        #upload-section {
+            margin-top: auto;
+            padding: 10px;
+            background-color: #fff;
+            border: 1px solid #ddd;
+            border-radius: 5px;
+        }
+
+        #upload-section input[type="file"] {
+            width: calc(100% - 120px);
+            margin-right: 10px;
+            padding: 5px;
+            font-size: 16px;
+        }
+
+        #upload-section button {
+            padding: 8px 16px;
+            font-size: 16px;
+            background-color: #007bff;
+            color: white;
+            border: none;
+            border-radius: 5px;
+            cursor: pointer;
+        }
+
+        #upload-section button:hover {
+            background-color: #0056b3;
+        }
+    </style>
+</head>
+<body>
+    <div id="sidebar">
+        <h2>分类</h2>
+        <ul id="category-list">
+            <!-- Categories will be dynamically loaded here -->
+        </ul>
+
+        <!-- File Upload Section (Now positioned at the bottom of the sidebar) -->
+        <div id="upload-section">
+            <h3>上传文件</h3>
+            <input type="file" id="file-input">
+            <button onclick="uploadFile()">上传</button>
+        </div>
+    </div>
+
+    <div id="content">
+        <h2>文件列表</h2>
+        <table>
+            <thead>
+                <tr>
+                    <th>ID</th>
+                    <th>Category Name</th>
+                    <th>操作</th>
+                </tr>
+            </thead>
+            <tbody id="table-body">
+                <!-- Data will be dynamically loaded here -->
+            </tbody>
+        </table>
+    </div>
+</body>
+</html>