triflix commited on
Commit
a84f31a
·
verified ·
1 Parent(s): 3464f08

Create main.py

Browse files
Files changed (1) hide show
  1. main.py +181 -0
main.py ADDED
@@ -0,0 +1,181 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import uuid
2
+ from pathlib import Path
3
+
4
+ import socketio
5
+ from fastapi import FastAPI, Request, UploadFile, File
6
+ from fastapi.responses import HTMLResponse, JSONResponse, FileResponse
7
+ from starlette.middleware.sessions import SessionMiddleware
8
+
9
+ # Create Socket.IO async server with ASGI mode
10
+ sio = socketio.AsyncServer(async_mode='asgi')
11
+ app = FastAPI()
12
+ app.add_middleware(SessionMiddleware, secret_key="your-secret-key")
13
+
14
+ # Mount the Socket.IO app with FastAPI
15
+ socket_app = socketio.ASGIApp(sio, other_asgi_app=app)
16
+
17
+ # Temporary directory for uploaded files
18
+ UPLOAD_DIR = Path("temp_uploads")
19
+ UPLOAD_DIR.mkdir(exist_ok=True)
20
+
21
+ @app.get("/", response_class=HTMLResponse)
22
+ async def index(request: Request):
23
+ html_content = """
24
+ <!DOCTYPE html>
25
+ <html>
26
+ <head>
27
+ <title>File Upload with Local History</title>
28
+ <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
29
+ <script src="https://cdn.socket.io/4.6.1/socket.io.min.js"></script>
30
+ </head>
31
+ <body class="bg-dark text-light">
32
+ <div class="container mt-5">
33
+ <!-- Card for File Upload -->
34
+ <div class="card bg-secondary mb-4">
35
+ <div class="card-header">
36
+ <h2>Upload File</h2>
37
+ </div>
38
+ <div class="card-body">
39
+ <form id="uploadForm">
40
+ <div class="mb-3">
41
+ <input type="file" id="fileInput" class="form-control">
42
+ </div>
43
+ <button type="submit" class="btn btn-primary">Upload</button>
44
+ </form>
45
+ <div class="progress mt-3" style="height: 25px; display: none;">
46
+ <div id="progressBar" class="progress-bar" role="progressbar" style="width: 0%;">0%</div>
47
+ </div>
48
+ <div id="downloadLink" class="mt-3"></div>
49
+ </div>
50
+ </div>
51
+ <!-- Card for Upload History -->
52
+ <div class="card bg-secondary">
53
+ <div class="card-header">
54
+ <h3>Your Upload History (Stored in Local Storage)</h3>
55
+ </div>
56
+ <div class="card-body">
57
+ <ul id="historyList" class="list-group"></ul>
58
+ <button id="refreshHistory" class="btn btn-secondary mt-2">Refresh History</button>
59
+ </div>
60
+ </div>
61
+ </div>
62
+ <script>
63
+ // Socket.IO connection
64
+ const socket = io();
65
+
66
+ socket.on('connect', () => {
67
+ console.log('Connected to Socket.IO server');
68
+ });
69
+
70
+ // Listen for progress updates
71
+ socket.on('upload_progress', data => {
72
+ const progressDiv = document.querySelector('.progress');
73
+ const progressBar = document.getElementById('progressBar');
74
+ progressDiv.style.display = 'block';
75
+ progressBar.style.width = data.percent + '%';
76
+ progressBar.innerText = data.percent + '%';
77
+ });
78
+
79
+ // Listen for upload completion
80
+ socket.on('upload_complete', data => {
81
+ const downloadLinkDiv = document.getElementById('downloadLink');
82
+ downloadLinkDiv.innerHTML = '<a href="' + data.download_url + '" class="text-info">Download File</a>';
83
+ storeInHistory(data.download_url);
84
+ refreshHistory();
85
+ });
86
+
87
+ // Upload file via XMLHttpRequest
88
+ document.getElementById('uploadForm').addEventListener('submit', function(event) {
89
+ event.preventDefault();
90
+ const fileInput = document.getElementById('fileInput');
91
+ const file = fileInput.files[0];
92
+ if (!file) {
93
+ alert("Please select a file!");
94
+ return;
95
+ }
96
+ const formData = new FormData();
97
+ formData.append('file', file);
98
+ const xhr = new XMLHttpRequest();
99
+ xhr.open('POST', '/upload', true);
100
+ xhr.send(formData);
101
+ });
102
+
103
+ // Local storage functions to store and retrieve history
104
+ function storeInHistory(downloadUrl) {
105
+ let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
106
+ history.push(downloadUrl);
107
+ localStorage.setItem('uploadHistory', JSON.stringify(history));
108
+ }
109
+
110
+ function refreshHistory() {
111
+ let history = JSON.parse(localStorage.getItem('uploadHistory')) || [];
112
+ const historyList = document.getElementById('historyList');
113
+ historyList.innerHTML = "";
114
+ history.forEach(link => {
115
+ const li = document.createElement('li');
116
+ li.className = 'list-group-item bg-dark text-light';
117
+ li.innerHTML = '<a href="' + link + '" class="text-info">' + link + '</a>';
118
+ historyList.appendChild(li);
119
+ });
120
+ }
121
+
122
+ // Refresh history on page load
123
+ window.onload = refreshHistory;
124
+ // Refresh history manually on button click
125
+ document.getElementById('refreshHistory').addEventListener('click', refreshHistory);
126
+ </script>
127
+ </body>
128
+ </html>
129
+ """
130
+ return HTMLResponse(content=html_content)
131
+
132
+ @app.post("/upload")
133
+ async def upload_file(request: Request, file: UploadFile = File(...)):
134
+ # Create or retrieve a unique session ID for the user (used for Socket.IO isolation)
135
+ session_id = request.session.get("id")
136
+ if not session_id:
137
+ session_id = str(uuid.uuid4())
138
+ request.session["id"] = session_id
139
+
140
+ # Generate a unique file ID and save the file in chunks
141
+ file_id = str(uuid.uuid4())
142
+ file_path = UPLOAD_DIR / file_id
143
+
144
+ CHUNK_SIZE = 1024 * 1024 # 1 MB
145
+ total = 0
146
+ content = await file.read()
147
+ total_size = len(content)
148
+ with file_path.open("wb") as f:
149
+ for i in range(0, total_size, CHUNK_SIZE):
150
+ chunk = content[i:i+CHUNK_SIZE]
151
+ f.write(chunk)
152
+ total += len(chunk)
153
+ percent = int((total / total_size) * 100)
154
+ await sio.emit('upload_progress', {'percent': percent}, to=session_id)
155
+
156
+ # Construct the download URL (using base URL and file_id)
157
+ base_url = request.url_for("index").rstrip("/")
158
+ download_url = f"{base_url}/download/{file_id}"
159
+
160
+ # Emit completion event with the download URL
161
+ await sio.emit('upload_complete', {'download_url': download_url}, to=session_id)
162
+
163
+ return JSONResponse({"download_url": download_url})
164
+
165
+ @app.get("/download/{file_id}")
166
+ async def download_file(file_id: str):
167
+ file_path = UPLOAD_DIR / file_id
168
+ if file_path.exists():
169
+ return FileResponse(file_path, filename="downloaded_file")
170
+ return JSONResponse({"error": "File not found"}, status_code=404)
171
+
172
+ @sio.event
173
+ async def connect(sid, environ):
174
+ # Use the socket id as the room identifier
175
+ await sio.save_session(sid, {"room": sid})
176
+ await sio.enter_room(sid, sid)
177
+ print(f"Client connected: {sid}")
178
+
179
+ @sio.event
180
+ async def disconnect(sid):
181
+ print(f"Client disconnected: {sid}")