實作後端
建立一個 FastAPI 專案,用來接收檔案。
| 12
 
 | mkdir xhr-upload-example-apicd xhr-upload-example-api
 
 | 
建立虛擬環境。
| 12
 
 | python -m venv .venvsource .venv/bin/activate
 
 | 
新增 requirements.txt 檔。
新增 ruff.toml 檔。
| 12
 3
 4
 5
 
 | line-length = 120indent-width = 4
 
 [format]
 quote-style = "double"
 
 | 
新增 .vscode/settings.json 檔。
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | {"[python]": {
 "editor.formatOnSave": true,
 "editor.codeActionsOnSave": {
 "source.fixAll": "explicit",
 "source.organizeImports": "explicit"
 },
 "editor.defaultFormatter": "charliermarsh.ruff"
 }
 }
 
 | 
新增 .gitignore 檔。
| 12
 3
 
 | __pycache__/.venv/
 files/
 
 | 
新增 main.py 檔。
| 12
 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
 
 | import os
 from fastapi import FastAPI, File, UploadFile
 from fastapi.middleware.cors import CORSMiddleware
 from fastapi.responses import JSONResponse
 
 FILE_DIR = "files"
 
 app = FastAPI()
 
 app.add_middleware(
 CORSMiddleware,
 allow_origins=["*"],
 allow_credentials=True,
 allow_methods=["*"],
 allow_headers=["*"],
 )
 
 if not os.path.exists(FILE_DIR):
 os.makedirs(FILE_DIR)
 
 
 @app.post("/upload")
 async def upload(file: UploadFile = File(...)):
 path = f"{FILE_DIR}/{file.filename}"
 
 with open(path, "wb") as f:
 f.write(file.file.read())
 
 return JSONResponse(content={"path": path})
 
 | 
啟動伺服器。
實作前端
| 12
 
 | mkdir xhr-upload-example-uicd xhr-upload-example-ui
 
 | 
新增 index.html 檔。
| 12
 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
 
 | <!DOCTYPE html><html lang="en">
 <head>
 <meta charset="UTF-8">
 <meta name="viewport" content="width=device-width, initial-scale=1.0">
 <title>Document</title>
 </head>
 <body>
 <input type="file" />
 <button type="button">Upload</button>
 <progress value="0" max="100" id="progress-bar"></progress>
 <script>
 const url = 'http://localhost:8000/upload';
 
 const inputElement = document.querySelector('input');
 const buttonElement = document.querySelector('button');
 const progressBarElement = document.getElementById('progress-bar');
 
 buttonElement.addEventListener('click', function() {
 const [file] = inputElement.files;
 const formData = new FormData();
 formData.append('file', file);
 
 const xhr = new XMLHttpRequest();
 xhr.open('POST', url, true);
 
 xhr.upload.onprogress = function(event) {
 if (event.lengthComputable) {
 progressBarElement.value = (event.loaded / event.total) * 100;
 }
 };
 
 xhr.send(formData);
 });
 </script>
 </body>
 </html>
 
 | 
啟動伺服器。
程式碼