1. 完成檔案生成瀏覽器下載功能

This commit is contained in:
shouchih_chen 2025-06-23 21:39:38 +08:00
parent 0b18e4b8ab
commit e329908d88
3 changed files with 72 additions and 29 deletions

51
app.py
View File

@ -1,8 +1,10 @@
import os import os
import uuid import uuid
from flask import Flask, render_template, request, send_file from flask import Flask, render_template, request, send_file
import subprocess import subprocess
from datetime import datetime
import zipfile
import io
app = Flask(__name__) app = Flask(__name__)
@ -24,6 +26,9 @@ def allowed_file(filename):
@app.route("/") @app.route("/")
def index(): def index():
search_str = 'e771'
return render_template("index.html") return render_template("index.html")
@app.route("/hello") @app.route("/hello")
def hello(): def hello():
@ -38,15 +43,42 @@ def upload_csv():
if file.filename == '': if file.filename == '':
return "No selected file", 400 return "No selected file", 400
if file and allowed_file(file.filename): if file and allowed_file(file.filename):
uuid_str = str(uuid.uuid4())[:4]
unique_filename = str(uuid.uuid4()) + '.csv' unique_filename = datetime.now().strftime("%Y%m%d_%H%M")+ '_' + uuid_str
csv_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename) csv_path = os.path.join(app.config['UPLOAD_FOLDER'], unique_filename+'.csv')
file.save(csv_path) file.save(csv_path)
print(f"CSV file saved to {csv_path}") print(f"CSV file saved to {csv_path}")
try: try:
# 呼叫外部 Python 腳本來生成圖片 subprocess.run([os.path.join(os.getcwd(), '.venv', 'Scripts', 'python'), 'generate_images.py', csv_path, unique_filename ], check=True)
subprocess.run([os.path.join(os.getcwd(), '.venv', 'Scripts', 'python'), 'generate_images.py', csv_path], check=True) folder = app.config['GENERATED_IMAGES_FOLDER']
matched_files = [f for f in os.listdir(folder) if uuid_str in f and f.endswith('.png')]
print(f"Matched files: {matched_files}")
# 壓縮所有圖片到一個 zip
zip_buffer = io.BytesIO()
with zipfile.ZipFile(zip_buffer, 'w') as zip_file:
for filename in matched_files:
file_path = os.path.join(folder, filename)
zip_file.write(file_path, arcname=filename)
zip_buffer.seek(0)
# 刪除生成的圖片文件
for filename in matched_files:
file_path = os.path.join(folder, filename)
os.remove(file_path)
print(f"Deleted file: {file_path}")
# 刪除 CSV 文件
os.remove(csv_path)
return send_file(
zip_buffer,
mimetype='application/zip',
as_attachment=True,
download_name=f'{unique_filename}_images.zip'
)
except subprocess.CalledProcessError as e: except subprocess.CalledProcessError as e:
print(f"Error generating images: {e}") print(f"Error generating images: {e}")
return "Error generating images", 500 return "Error generating images", 500
@ -56,9 +88,8 @@ def upload_csv():
except Exception as e: except Exception as e:
print(f"An unexpected error occurred: {e}") print(f"An unexpected error occurred: {e}")
return "An unexpected error occurred", 500 return "An unexpected error occurred", 500
return "File uploaded successfully", 200
return "File uploaded successfully", 200
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -101,7 +101,7 @@ class ImageGenerator(QObject):
self.stop() self.stop()
@Slot() @Slot()
def generate_images(self, name_csv): def generate_images(self, name_csv, out_filename):
# Placeholder for image generation logic # Placeholder for image generation logic
print("Generating images...1") print("Generating images...1")
@ -161,7 +161,7 @@ class ImageGenerator(QObject):
if i > 0 and i % 6 == 5: if i > 0 and i % 6 == 5:
# Save the current scene to an image file every 5 items # Save the current scene to an image file every 5 items
self.scene_to_image(self.scene, f"generated_image_{i//6}.png") self.scene_to_image(self.scene, f"{out_filename}_{i//6}.png")
self.scene.clear() self.scene.clear()
print("Image generated and added to scene.") print("Image generated and added to scene.")
@ -177,17 +177,19 @@ if __name__ == "__main__":
generator = ImageGenerator() generator = ImageGenerator()
generator.finished.connect(app.quit) generator.finished.connect(app.quit)
if len(sys.argv) > 1: csv_file = "resource/sample.csv"
out_filename = "generated_image"
if len(sys.argv) > 2:
csv_file = sys.argv[1] csv_file = sys.argv[1]
print(f"Loading CSV file: {csv_file}") out_filename = sys.argv[2]
else: print(f"Loading CSV file: {csv_file}, Output filename: {out_filename}")
csv_file = "resource/sample.csv"
print(f"No CSV file provided, using default: {csv_file}")
sample_data = generator.read_csv(csv_file) sample_data = generator.read_csv(csv_file)
print(f"Sample data loaded: {sample_data}") print(f"Sample data loaded: {sample_data}")
generator.generate_images(sample_data) generator.generate_images(sample_data, out_filename)
#generator.start() #generator.start()
# Run the application event loop # Run the application event loop

View File

@ -3,13 +3,13 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>CSV 轉圖片工具</title> <title>圓形鑰匙圈產生器</title>
</head> </head>
<body> <body>
<h1>上傳 CSV 產生圖片</h1> <h1>圓形鑰匙圈產生器</h1>
<form id="uploadForm" enctype="multipart/form-data"> <form id="uploadForm" enctype="multipart/form-data">
<input type="file" id="csvFile" name="csvFile" accept=".csv"> <input type="file" id="csvFile" name="csvFile" accept=".csv">
<button type="submit">生成圖片</button> <!-- <button type="submit">生成圖片</button> -->
</form> </form>
<div id="status"></div> <div id="status"></div>
<div id="downloadLink" style="display: none;"> <div id="downloadLink" style="display: none;">
@ -36,8 +36,13 @@
const formData = new FormData(); const formData = new FormData();
formData.append('csvFile', fileInput.files[0]); formData.append('csvFile', fileInput.files[0]);
// 產生日期時間字串
const now = new Date();
const pad = n => n.toString().padStart(2, '0');
const datetimeStr = `${now.getFullYear()}${pad(now.getMonth()+1)}${pad(now.getDate())}_${pad(now.getHours())}${pad(now.getMinutes())}`;
try { try {
const response = await fetch('/upload_csv', { // 請確保後端路由與此匹配 const response = await fetch('/upload_csv', {
method: 'POST', method: 'POST',
body: formData body: formData
}); });
@ -47,21 +52,26 @@
throw new Error(`伺服器錯誤: ${response.status} - ${errorText}`); throw new Error(`伺服器錯誤: ${response.status} - ${errorText}`);
} }
// 假設後端會返回圖片的 Blob 或直接是檔案的 URL // 取得 zip blob 並建立下載連結
// 如果後端直接返回圖片檔案,可以直接創建 Blob URL const zipBlob = await response.blob();
const imageBlob = await response.blob(); const zipUrl = URL.createObjectURL(zipBlob);
const imageUrl = URL.createObjectURL(imageBlob);
downloadAnchor.href = imageUrl; downloadAnchor.href = zipUrl;
downloadAnchor.download = 'generated_image.png'; // 可以從後端獲取圖片名稱 downloadAnchor.download = `${datetimeStr}_圓形鑰匙產生器.zip`; // zip 檔名加上日期時間
statusDiv.textContent = '圖片已成功生成!'; statusDiv.textContent = '圖片已成功生成!請點擊下方連結下載壓縮檔。';
downloadLinkDiv.style.display = 'block'; downloadLinkDiv.style.display = 'block';
downloadAnchor.click(); // 自動觸發下載
} catch (error) { } catch (error) {
console.error('上傳或生成圖片失敗:', error); console.error('上傳或生成圖片失敗:', error);
statusDiv.textContent = `生成圖片失敗: ${error.message || '未知錯誤'}`; statusDiv.textContent = `生成圖片失敗: ${error.message || '未知錯誤'}`;
} }
}); });
document.getElementById('csvFile').addEventListener('change', function() {
// 自動觸發表單提交
document.getElementById('uploadForm').dispatchEvent(new Event('submit', {cancelable: true, bubbles: true}));
});
</script> </script>
</body> </body>
</html> </html>