I wrote the following flask application which will collect a file selected and then upload it to a database running in sql lite. If i run the app locally, it works fine with the table created in the database and all the data uploaded. However if I run the app using docker, I get a 200 on file upload but do not see the table created in the database along with all the data.
Here is the code:
import os
import sqlite3
from flask import Flask, flash, jsonify, request, redirect, render_template
from werkzeug.utils import secure_filename
app = Flask(__name__)
app.secret_key = "secret key"
app.config['MAX_CONTENT_LENGTH'] = 16 * 1024 * 1024
path = os.getcwd()
uploadFolder = 'app/uploads'
os.makedirs(uploadFolder, exist_ok=True)
app.config['UPLOAD_FOLDER'] = uploadFolder
allowedExtensions = set(['txt'])
# Initialize SQLite database
#database = os.path.join(path, 'file_uploads.db')
database = 'app/uploads/file_uploads.db'
# Health check to see if the service is active
@app.route('/healthCheck', methods=['GET'])
def checkStatus():
response = {
'healthCheck': 'Flask service is up and running!'
}
return jsonify(response), 200
def createTable():
conn = sqlite3.connect(database)
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS words
(id INTEGER PRIMARY KEY AUTOINCREMENT,
word TEXT NOT NULL,
filename TEXT NOT NULL,
filepath TEXT NOT NULL)''')
conn.commit()
conn.close()
createTable()
def allowedFile(filename):
return '.' in filename and filename.rsplit('.', 1)[1].lower() in allowedExtensions
@app.route('/')
def uploadForm():
return render_template('upload.html')
@app.route('/', methods=['POST'])
def uploadFile():
if request.method == 'POST':
if 'file' not in request.files:
flash('No file part')
return redirect(request.url)
file = request.files['file']
if file.filename == '':
flash('No file selected for uploading')
return redirect(request.url)
if file and allowedFile(file.filename):
filename = secure_filename(file.filename)
filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
file.save(filepath)
# Read content of the file and split into words
with open(filepath, 'r') as f:
content = f.read()
words = content.split()
# Insert each word into the SQLite database
conn = sqlite3.connect(database)
c = conn.cursor()
for word in words:
c.execute("INSERT INTO words (word, filename, filepath) VALUES (?, ?, ?)", (word, filename, filepath))
conn.commit()
conn.close()
flash('File successfully uploaded and words saved to database')
return render_template('upload.html')
else:
flash('Allowed file types are txt')
return redirect(request.url)
# Route to get word by ID
@app.route('/word/<int:id>', methods=['GET'])
def getWordById(id):
conn = sqlite3.connect(database)
c = conn.cursor()
c.execute("SELECT word FROM words WHERE id=?", (id,))
word = c.fetchone()
conn.close()
if word:
return jsonify({'id': id, 'word': word[0]}), 200
else:
return jsonify({'error': 'Word not found'}), 404
if __name__ == "__main__":
app.run(host="127.0.0.1", port=5000)
Can someone please help me figure out why the table is not visible in sql lite even though 200 appears in the log for the file upload.
Here is the expected output:
I am unable to accompolish the expected output in SQLLite when running the app using Docker. What am I doing wrong?
Here are the contents of the docker file:
# Use an official Python runtime as a parent image
FROM python:3.8-slim
# Set the working directory in the container
WORKDIR /app
# Copy the Flask application directory into the container at /app
COPY . /app
# Install any needed dependencies specified in requirements.txt
RUN pip install --no-cache-dir -r requirements.txt
# Make port 5000 available to the world outside this container
EXPOSE 5000
# Set the upload folder as a volume
VOLUME /app/uploads
# Define environment variable
ENV FLASK_APP=test.py
# Run the Flask application when the container launches
CMD ["flask", "run", "--host=0.0.0.0"]
Makefile Code:
# Variables
DOCKER_IMAGE_NAME = my-flask-app
DOCKER_CONTAINER_NAME = my-flask-container
TEST_FILE = upload/word.txt
# Targets
build:
docker build -t $(DOCKER_IMAGE_NAME) .
run:
docker run -d -p 5000:5000 --name $(DOCKER_CONTAINER_NAME) $(DOCKER_IMAGE_NAME)
test: build
docker run $(DOCKER_IMAGE_NAME) python -u test.py
upload:
curl -X POST -F "file=@$(TEST_FILE)" http://127.0.0.1:5000/
stop:
docker stop $(DOCKER_CONTAINER_NAME)
docker rm $(DOCKER_CONTAINER_NAME)
.PHONY: build run stop test upload
I have noticed two places where your application could throw a possible error.
First, your upload folder is not created as intended.
You are trying to create a folder with the path app/uploads
. So within your project directory, a folder app
with a subfolder uploads
.
To create a complete folder structure, I recommend os.makedirs(path, exist_ok=True)
. This also eliminates the need to check whether the folder already exists.
# ...
uploadFolder = 'app/uploads'
os.makedirs(uploadFolder, exist_ok=True)
app.config['UPLOAD_FOLDER'] = uploadFolder
# ...
Secondly, this results in a different path specification for the volume within your docker file, since you create the directory structure app/uploads
mentioned within your working directory /app
.
VOLUME /app/app/uploads
With the changes mentioned, the application should run without errors.
However, I noticed that you are storing your database in the folder where your uploads are stored. This may work in a test environment. For your productive application, I would recommend separating this. The instance path may be a sensible alternative as a storage location for the database and a subfolder for the uploads.