Do you have any ways to make the frontend wait for the backend to execute the image compression and send the URL, and then display a download button for the client to download?
Overview:
I’ve built a simple website to compress images using Huffman coding (following the guide at https://narainsreehith.medium.com/huffman-coding-on-image-d6092bed5821).
When a client uploads an image, I use JavaScript and Flask to POST the image to the server.
After that, the backend compresses the image using Python and sends the URL link to the client for downloading the compressed image.
Problem:
I can POST the image, and the server can receive it and store it in the uploads folder.
The compression code works well, and the output image is stored in the output folder.
However, when I upload an image and click the submit button (to compress), the page reloads and does not wait for the backend to send the URL. I cannot fix this.
This is my code:
app.py
from flask import Flask, render_template, request, jsonify, send_from_directory
import os
from io import BytesIO
import numpy as np
import cv2
from copy import deepcopy as cp
from concurrent.futures import ThreadPoolExecutor
from flask_cors import CORS
app = Flask(__name__)
CORS(app)
app.config["UPLOAD_FOLDER"] = "uploads"
app.config["COMPRESSED_FOLDER"] = "compressed"
app.config["OUTPUT_FOLDER"] = "output"
executor = ThreadPoolExecutor()
class Node:
pass # class node
class Image:
pass # class image to compress image
@app.route("/")
def index():
return render_template("image_compressor.html")
def process_image(file_path, file_name):
image = Image()
compressed_path = os.path.join(
app.config["COMPRESSED_FOLDER"], f"compressed_{file_name}.bin"
)
output_path = os.path.join(
app.config["OUTPUT_FOLDER"], f"compressed_{file_name}.jpg"
)
image.huffmanCode(file_path, compressed_path, output_path, toCheck=1)
@app.route("/upload", methods=["POST"])
def upload_image():
if "image-upload" not in request.files:
return jsonify({"error": "No file part"}), 400
file = request.files["image-upload"]
if file.filename == "":
return jsonify({"error": "No selected file"}), 400
if file:
file_path = os.path.join(app.config["UPLOAD_FOLDER"], file.filename)
file.save(file_path)
file_name = file.filename.split(".")[0]
executor.submit(process_image, file_path, file_name)
return jsonify({"message": "Processing initiated", "filename": file.filename})
@app.route("/status/<filename>", methods=["GET"])
def status(filename):
file_name = filename.split(".")[0]
output_path = os.path.join(
app.config["OUTPUT_FOLDER"], f"compressed_{file_name}.jpg"
)
if os.path.exists(output_path):
return jsonify({"status": "done", "url": f"/download/{filename}"})
else:
return jsonify({"status": "processing"})
@app.route("/download/<filename>", methods=["GET"])
def download_file(filename):
file_name = filename.split(".")[0]
output_path = os.path.join(
app.config["OUTPUT_FOLDER"], f"compressed_{file_name}.jpg"
)
if os.path.exists(output_path):
return send_from_directory(
directory=app.config["OUTPUT_FOLDER"], path=f"compressed_{file_name}.jpg"
)
else:
return "File not found", 404
if __name__ == "__main__":
for folder in [
app.config["UPLOAD_FOLDER"],
app.config["COMPRESSED_FOLDER"],
app.config["OUTPUT_FOLDER"],
]:
if not os.path.exists(folder):
os.makedirs(folder)
app.run(debug=True)
javascript file
document.getElementById('uploadForm').addEventListener('submit', function (event) {
event.preventDefault(); // Prevent the default form submission
var formData = new FormData(this);
var uploadButton = event.target.querySelector('button[type="submit"]');
uploadButton.textContent = "Uploading..."; // Indicate upload progress
uploadButton.disabled = true;
console.log('http://127.0.0.1:5000/upload')
fetch('http://127.0.0.1:5000/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.error) {
throw new Error(data.error);
}
var filename = data.filename;
checkStatus(filename, uploadButton);
})
.catch(error => {
console.error('Error:', error);
alert('There was an error processing your request.');
uploadButton.textContent = "Compress";
uploadButton.disabled = false;
});
});
function checkStatus(filename, uploadButton) {
console.log(`http://127.0.0.1:5000/status/${filename}`)
fetch(`http://127.0.0.1:5000/status/${filename}`)
.then(response => response.json())
.then(data => {
if (data.status === "done") {
var downloadLink = document.getElementById('downloadLink');
var downloadButton = document.getElementById('downloadButton');
downloadButton.href = data.url;
downloadButton.download = filename;
downloadLink.style.display = 'block';
uploadButton.textContent = "Compress";
uploadButton.disabled = false;
} else {
setTimeout(() => checkStatus(filename, uploadButton), 2000); // Poll every 2 seconds
}
})
.catch(error => {
console.error('Error:', error);
alert('There was an error processing your request.');
uploadButton.textContent = "Compress";
uploadButton.disabled = false;
});
}
html file
<section class="container compressor">
<div class="background">
<div class="heading_wrapper">
<h1 class="heading">Fast & efficient image <span class="highlight">compression</span></h1>
</div>
<form class="upload_wrapper" id="uploadForm">
<div class="upload_presentation">
<input class="upload" id="image-upload" type="file" name="image-upload" accept=".jpg, .png" />
<label for="image-upload">
<i class="fa-solid fa-upload upload_icon"></i>
<span>Choose a file</span>
</label>
</div>
<div class="button_wrapper">
<button type="submit" class="btn btn_compress">Compress</button>
</div>
<div id="downloadLink" style="display:none;">
<a id="downloadButton" class="btn btn_download" href="#" download>Download Compressed Image</a>
</div>
</form>
</div>
</section>
Thank you all.
user25285481 is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.