flask file upload working
This commit is contained in:
parent
71815a6533
commit
f11dd091f1
4
backend/.env
Normal file
4
backend/.env
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
SECRET_KEY=MVPhXY*yNXPE2MbXDdpxXk
|
||||||
|
REDIS_PASSWORD=eYVX7EwVmmxKPCDmwMtyKVge8oLd2t81
|
||||||
|
POSTGRESQL_DATABASE_URL='postgresql+psycopg2://arufa:Plop2099@localhost:5432/arufalab'
|
||||||
|
# POSTGRESQL_DATABASE_URL => 'postgresql+psycopg2://arufa:password@host:port/database'
|
||||||
BIN
backend/__pycache__/config.cpython-312.pyc
Normal file
BIN
backend/__pycache__/config.cpython-312.pyc
Normal file
Binary file not shown.
BIN
backend/__pycache__/models.cpython-312.pyc
Normal file
BIN
backend/__pycache__/models.cpython-312.pyc
Normal file
Binary file not shown.
@ -21,8 +21,16 @@ app.config["SESSION_TYPE"] = 'redis'
|
|||||||
app.config["SESSION_PERMANENT"] = False
|
app.config["SESSION_PERMANENT"] = False
|
||||||
app.config["SESSION_USE_SIGNER"] = True
|
app.config["SESSION_USE_SIGNER"] = True
|
||||||
app.config["SESSION_REDIS"] = redis.from_url("redis://192.168.50.107:6379")
|
app.config["SESSION_REDIS"] = redis.from_url("redis://192.168.50.107:6379")
|
||||||
|
# upload directory
|
||||||
|
app.config["UPLOAD_DIRECTORY"] = 'uploads/'
|
||||||
# app.config["REDIS_PASSWORD"] = os.environ["REDIS_PASSWORD"]
|
# app.config["REDIS_PASSWORD"] = os.environ["REDIS_PASSWORD"]
|
||||||
|
|
||||||
|
# File value limit
|
||||||
|
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024 # 16MB
|
||||||
|
|
||||||
|
# List of allowed file extensions
|
||||||
|
app.config['ALLOWED_EXTENSIONS'] = ['.jpg', '.jpeg', '.png', '.gif']
|
||||||
|
|
||||||
db = SQLAlchemy(app)
|
db = SQLAlchemy(app)
|
||||||
|
|
||||||
server_session = Session(app)
|
server_session = Session(app)
|
||||||
|
|||||||
BIN
backend/instance/mydatabase.db
Normal file
BIN
backend/instance/mydatabase.db
Normal file
Binary file not shown.
@ -1,16 +1,45 @@
|
|||||||
from flask import Flask, render_template, request
|
from flask import Flask, render_template, request, redirect, send_from_directory
|
||||||
|
from config import app, db, bcrypt
|
||||||
|
from models import Contact, User
|
||||||
|
from werkzeug.utils import secure_filename
|
||||||
|
from werkzeug.exceptions import RequestEntityTooLarge
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
@app.route('/')
|
@app.route('/')
|
||||||
def index():
|
def index():
|
||||||
return render_template('index.html')
|
files = os.listdir(app.config['UPLOAD_DIRECTORY'])
|
||||||
|
images = []
|
||||||
@app.route('/api/upload', methods=['POST'])
|
|
||||||
def upload():
|
|
||||||
file = request.files['file']
|
|
||||||
|
|
||||||
# save the file
|
for file in files:
|
||||||
file.save(f'uploads/{file.filename}')
|
if os.path.splitext(file)[1].lower() in app.config['ALLOWED_EXTENSIONS']:
|
||||||
|
images.append(file)
|
||||||
|
return render_template('index.html', images=images)
|
||||||
|
|
||||||
|
@app.route('/upload', methods=["POST"])
|
||||||
|
def upload():
|
||||||
|
try:
|
||||||
|
file = request.files['file']
|
||||||
|
file_extension = os.path.splitext(file.filename)[1] # [0] is the filename
|
||||||
|
|
||||||
|
# save the file if it exists
|
||||||
|
if file:
|
||||||
|
if file_extension not in app.config['ALLOWED_EXTENSIONS']:
|
||||||
|
return 'File is not an image'
|
||||||
|
file.save(os.path.join(
|
||||||
|
app.config['UPLOAD_DIRECTORY'],
|
||||||
|
secure_filename(file.filename)
|
||||||
|
))
|
||||||
|
except RequestEntityTooLarge: # except Exception would catch all errors instead of specified
|
||||||
|
return 'File is larger than the limit of 16mb.'
|
||||||
|
|
||||||
|
return redirect('/')
|
||||||
|
|
||||||
|
# Handle Images
|
||||||
|
@app.route('/serve-image/<filename>', methods=['GET'])
|
||||||
|
def serve_image(filename):
|
||||||
|
return send_from_directory(app.config['UPLOAD_DIRECTORY'], filename)
|
||||||
|
|
||||||
|
|
||||||
# Main
|
# Main
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
34
backend/models.py
Normal file
34
backend/models.py
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
from config import db
|
||||||
|
from uuid import uuid4
|
||||||
|
|
||||||
|
class Contact(db.Model):
|
||||||
|
id = db.Column(db.Integer, primary_key=True)
|
||||||
|
first_name = db.Column(db.String(80), unique=False, nullable=False)
|
||||||
|
last_name = db.Column(db.String(80), unique=False, nullable=False)
|
||||||
|
email = db.Column(db.String(120), unique=False, nullable=False)
|
||||||
|
|
||||||
|
def to_json(self):
|
||||||
|
return {
|
||||||
|
"id": self.id,
|
||||||
|
"firstName":self.first_name,
|
||||||
|
"lastName":self.last_name,
|
||||||
|
"email": self.email,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def get_uuid():
|
||||||
|
return uuid4().hex
|
||||||
|
|
||||||
|
class User(db.Model):
|
||||||
|
__tablename__ = "users"
|
||||||
|
id = db.Column(db.String(32), primary_key=True, unique=True, default=get_uuid)
|
||||||
|
email = db.Column(db.String(345), unique=True)
|
||||||
|
password = db.Column(db.Text, nullable=False)
|
||||||
|
|
||||||
|
# class Category(db.Model):
|
||||||
|
# __tablename__ = "categories"
|
||||||
|
# id = db.Column(db.String(32), primary_key=True, unique=True, default=get_uuid)
|
||||||
|
# name = db.Column(db.String(345), default='New Shirt')
|
||||||
|
# number = db.Column(db.Integer)
|
||||||
|
# sale = db.Column(db.Boolean, default=False)
|
||||||
|
|
||||||
@ -4,4 +4,7 @@ flask-bcrypt
|
|||||||
flask-cors
|
flask-cors
|
||||||
python-dotenv
|
python-dotenv
|
||||||
flask-session
|
flask-session
|
||||||
redis
|
redis
|
||||||
|
Flask-Migrate
|
||||||
|
flask_validator
|
||||||
|
psycopg2-binary
|
||||||
130
backend/static/styles.css
Normal file
130
backend/static/styles.css
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
:root {
|
||||||
|
--black: #000000;
|
||||||
|
--almost-black: #080708;
|
||||||
|
--blue: #3772FF;
|
||||||
|
--blue-light: #3787ff;
|
||||||
|
--red: #DF2935;
|
||||||
|
--yellow: #FDCA40;
|
||||||
|
--light: #E6E8E6;
|
||||||
|
--white: #FFFFFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
* {
|
||||||
|
position: relative;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
||||||
|
font-size: 22px;
|
||||||
|
line-height: 1.5;
|
||||||
|
color: var(--white);
|
||||||
|
background: var(--black);
|
||||||
|
background-image: linear-gradient(to bottom right, var(--almost-black), var(--black));
|
||||||
|
}
|
||||||
|
|
||||||
|
.app {
|
||||||
|
height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
padding: 15px 0 0 15px;
|
||||||
|
width: 25%;
|
||||||
|
color: var(--light);
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images-section {
|
||||||
|
padding: 15px 0 0 15px;
|
||||||
|
width: 75%;
|
||||||
|
height: 100vh;
|
||||||
|
align-content: flex-start;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
display: flex;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images-section a {
|
||||||
|
margin: 0 15px 15px 0;
|
||||||
|
padding-bottom: 20%;
|
||||||
|
content: '';
|
||||||
|
width: calc(33.33% - 15px);
|
||||||
|
height: 0;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images-section img {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 4px;
|
||||||
|
object-fit: cover;
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 1200px) {
|
||||||
|
|
||||||
|
.app {
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-section {
|
||||||
|
padding-right: 15px;
|
||||||
|
padding-bottom: 15px;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.images-section {
|
||||||
|
padding-top: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 800px) {
|
||||||
|
|
||||||
|
.images-section a {
|
||||||
|
padding-bottom: 60%;
|
||||||
|
width: calc(100% - 15px);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Forms */
|
||||||
|
form {
|
||||||
|
width: 100%;
|
||||||
|
flex-direction: column;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='file'] {
|
||||||
|
margin: 0 0 12px;
|
||||||
|
padding: 10px 10px;
|
||||||
|
border-radius: 4px;
|
||||||
|
font-size: 22px;
|
||||||
|
color: var(--almost-black);
|
||||||
|
background: var(--white);
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit'],
|
||||||
|
button[type='button'] {
|
||||||
|
padding: 13px 20px;
|
||||||
|
font-size: 22px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: var(--white);
|
||||||
|
border: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
box-shadow: none;
|
||||||
|
cursor: pointer;
|
||||||
|
background: var(--blue);
|
||||||
|
background-image: linear-gradient(to bottom, var(--blue-light), var(--blue));
|
||||||
|
transition: box-shadow .2s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
input[type='submit']:hover,
|
||||||
|
button[type='button']:hover {
|
||||||
|
box-shadow: inset 0 -25px 25px var(--blue-light);
|
||||||
|
}
|
||||||
@ -11,20 +11,20 @@
|
|||||||
<div class="app">
|
<div class="app">
|
||||||
|
|
||||||
<div class="form-section">
|
<div class="form-section">
|
||||||
<form aciton="/api/upload" method="post" enctype="multipart/form-data">
|
|
||||||
<input type="file" name="file">
|
|
||||||
<input type="submit" name="Upload">
|
|
||||||
|
|
||||||
</form>
|
|
||||||
|
|
||||||
<!-- Form will go here -->
|
<form action="/upload" method="post" enctype="multipart/form-data">
|
||||||
|
<input type="file" name="file">
|
||||||
|
<input type="submit" value="Upload">
|
||||||
|
</form>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="images-section">
|
<div class="images-section">
|
||||||
|
{% for image in images %}
|
||||||
<!-- Images will go here -->
|
<a href="{{ url_for('serve_image', filename=image) }}" target="_blank">
|
||||||
|
<img src="{{ url_for('serve_image', filename=image) }}"/>
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user