Uploading and download files in web browser is a common task in virtually any web application or service. This article shows how to do this with very little coding - in less than 100 lines of code. The database used is PostgreSQL, and the web server is Nginx.
You will use Gliimly as an application server and the programming language. It will run behind the web server for performance and security, as well as to enable richer web functionality. This way end-user cannot talk to your application server directly because all such requests go through the web server, while your back-end application can talk directly to your application server for better performance.
Assuming your currently logged-on Linux user will own the application, create a source code directory and also create Gliimly application named "file-manager":
mkdir filemgr
cd filemgr
gg -k file-manager
Next, create PostgreSQL database named "db_file_manager", owned by currently logged-on user (i.e. passwordless setup):
echo "create user $(whoami);
create database db_file_manager with owner=$(whoami);
grant all on database db_file_manager to $(whoami);
\q" | sudo -u postgres psql
Create database configuration file used by Gliimly that describes the database (it's a file "db"):
echo "user=$(whoami) dbname=db_file_manager" > db
Create SQL table that will hold files currently stored on the server:
echo "create table if not exists files (fileName varchar(100), localPath varchar(300), extension varchar(10), description varchar(200), fileSize int, fileID bigserial primary key);" | psql -d db_file_manager
Finally, create source Gliimly files. First create "start.gliim" file and copy and paste:
begin-handler /start public
@<h2>File Manager</h2>
@To manage the uploaded files, <a href="<<p-path "/list">>">click here.</a><br/>
@<br/>
@<form action="<<p-path "/upload">>" method="POST" enctype="multipart/form-data">
@ <label for="file_description">File description:</label><br>
@ <textarea name="filedesc" rows="3" columns="50"></textarea><br/>
@ <br/>
@ <label for="filename">File:</label>
@ <input type="file" name="file" value=""><br><br>
@ <input type="submit" value="Submit">
@</form>
end-handler
Create "list.gliim" file and copy and paste:
begin-handler /list public
@<h2>List of files</h2>
@To add a file, <a href="<<p-path "/start">>">click here</a><br/><br/>
@<table border="1">
@<tr>
@ <td>File</td><td>Description</td><td>Size</td><td>Show</td><td>Delete</td>
@</tr>
run-query @db= \
"select fileName, description, fileSize, fileID from files order by fileSize desc" \
output file_name, description noencode, file_size, file_ID
@<tr>
@ <td><<p-web file_name>></td><td><<p-web description>><td><<p-web file_size>></td>
@ <td><a href="<<p-path "/download">>/file_id=<<p-url file_ID>>">Show</a></td>
@ <td><a href="<<p-path "/delete">>/action=confirm/file_id=<<p-url file_ID>>">Delete</a></td>
@</tr>
end-query
@</table>
end-handler
Create "upload.gliim" file and copy and paste:
begin-handler /upload public
get-param filedesc // file description from the upload form
get-param file_filename // file name
get-param file_location // the path to uploaded file
get-param file_size // size in bytes
get-param file_ext // the file extension
@<h2>Uploading file</h2>
run-query @db= \
"insert into files (fileName, localPath, extension, description, fileSize) \
values ('%s', '%s', '%s', '%s', '%s')" \
input file_filename, file_location, file_ext, filedesc, file_size
end-query
@File <<p-web file_filename>> of size <<p-web file_size>> \
is stored on server at <<p-web file_location>>. \
File description is <<p-web filedesc>>.<hr/>
end-handler
Create "download.gliim" file and copy and paste:
begin-handler /download public
get-param file_id
run-query @db= \
"select localPath,extension from files where fileID='%s'" \
input file_id \
output local_path, ext \
row-count num_files
if-true ext equal ".jpg"
send-file local_path headers content-type "image/jpg"
else-if ext equal ".pdf"
send-file local_path headers content-type "application/pdf"
else-if
send-file local_path headers content-type "application/octet-stream" download
end-if
end-query
if-true num_files not-equal 1
@Cannot find this file!<hr/>
exit-handler
end-if
end-handler
Create "delete.gliim" file and copy and paste:
begin-handler /delete public
@<h2>Delete a file</h2>
get-param action
get-param file_id
run-query @db="select fileName, localPath, description from files where fileID='%s'" output file_name, local_path, desc input file_id
if-true action equal "confirm" // get file information to confirm what will be deleted
@Are you sure you want to delete file <<p-web file_name>> (<<p-web desc>>)? Click <a href="<<p-path "/delete">>?action=delete&file_id=<<p-url file_id>>">Delete</a> or click the browser's Back button to go back.<br/>
else-if action equal "delete" // actual delete file, once confirmed
begin-transaction @db
run-query @db= "delete from files where fileID='%s'" input file_id error err no-loop
if-true err not-equal "0"
@Could not delete the file (error <<p-web err>>)
rollback-transaction @db
else-if
delete-file local_path status st
if-true st equal GG_OKAY
commit-transaction @db
@File deleted. Go back to <a href="<<p-path "/start">>">start page</a>
else-if
rollback-transaction @db
@File could not be deleted, error <<p-num st>>
end-if
end-if
else-if
@Unrecognized action <<p-web action>>
end-if
end-query
end-handler
Make the application:
gg -q --db=postgres:db
Run your application server:
mgrg file-manager
A web server sits in front of Gliimly application server, so it needs to be setup. This example is for Ubuntu, so edit Nginx config file there:
sudo vi /etc/nginx/sites-enabled/default
Add this in "server {}" section ("client_max_body_size" allows for images of typical sizes to be uploaded):
location /file-manager/ { include /etc/nginx/fastcgi_params; fastcgi_pass unix:///var/lib/gg/file-manager/sock/sock; }
client_max_body_size 100M;
Restart Nginx:
sudo systemctl restart nginx
Go to your web browser, and enter:
http://127.0.0.1/file-manager/start
This is what the end result looks like. Obviously, we used just bare-bone HTML, but that's not the point here at all. You can use any kind of front-end technology, the point is to demonstrate Gliimly as a back-end server for web applications/services.
Here's the home screen, with the form to upload a file and a link to liFilest of files:
Listing files:
Asking to delete a file:
Confirmation of deletion: