{"id":14065468,"url":"https://github.com/nickjj/flask-static-digest","last_synced_at":"2025-05-15T23:07:52.575Z","repository":{"id":49502107,"uuid":"218601903","full_name":"nickjj/flask-static-digest","owner":"nickjj","description":"Flask extension to help make your static files production ready by md5 tagging and gzipping them.","archived":false,"fork":false,"pushed_at":"2024-12-07T18:38:06.000Z","size":78,"stargazers_count":159,"open_issues_count":5,"forks_count":26,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-05-08T20:50:13.381Z","etag":null,"topics":["flask","flask-extension","gzipped-files","md5-digest","python","static-files"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nickjj.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":"nickjj","custom":["https://www.paypal.me/nickjanetakis"]}},"created_at":"2019-10-30T19:02:54.000Z","updated_at":"2025-03-22T23:28:43.000Z","dependencies_parsed_at":"2025-01-11T15:06:56.291Z","dependency_job_id":"565f92f0-07a1-4890-8bca-0104db7ec71b","html_url":"https://github.com/nickjj/flask-static-digest","commit_stats":{"total_commits":65,"total_committers":4,"mean_commits":16.25,"dds":0.0461538461538461,"last_synced_commit":"edd8bee96737c0cdecde236f65740208f339c342"},"previous_names":[],"tags_count":10,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Fflask-static-digest","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Fflask-static-digest/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Fflask-static-digest/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nickjj%2Fflask-static-digest/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nickjj","download_url":"https://codeload.github.com/nickjj/flask-static-digest/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254436949,"owners_count":22070947,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["flask","flask-extension","gzipped-files","md5-digest","python","static-files"],"created_at":"2024-08-13T07:04:30.527Z","updated_at":"2025-05-15T23:07:46.935Z","avatar_url":"https://github.com/nickjj.png","language":"Python","funding_links":["https://github.com/sponsors/nickjj","https://www.paypal.me/nickjanetakis"],"categories":["Python"],"sub_categories":[],"readme":"# What is Flask-Static-Digest? ![CI](https://github.com/nickjj/flask-static-digest/workflows/CI/badge.svg?branch=master)\n\nIt is a Flask extension that will help make your static files production ready\nwith very minimal effort on your part. It does this by creating md5 tagged\nversions and gzip and / or brotli compressed versions of your static files by\nrunning a `flask digest compile` command that this extension adds to your Flask\napp.\n\nIt should be the last thing you do to your static files before uploading them\nto your server or CDN. Speaking of which, if you're using a CDN this extension\noptionally lets you configure a host URL that will get prepended to your static\nfile paths. If you're not using a CDN, no problem everything will work as you\nwould expect by default.\n\nOther web frameworks like Django, Ruby on Rails and Phoenix all have this\nfeature built into their framework, and now with this extension Flask does too.\n\n**This extension will work if you're not using any asset build tools but at the\nsame time it also works with esbuild, Webpack, Grunt, Gulp or any other build\ntool you can think of. This tool does not depend on or compete with existing\nasset build tools.**\n\nIf you're already using Webpack or a similar tool, that's great. Webpack takes\ncare of bundling your assets and helps convert things like SASS to CSS and ES6+\nJS to browser compatible JS. That is solving a completely different problem\nthan what this extension solves. This extension will further optimize your\nstatic files after your build tool produces its output files.\n\nThis extension does things that Webpack alone cannot do because in order for\nthings like md5 tagging to work Flask needs to be aware of how to map those\nhashed file names back to regular file names you would reference in your Jinja\n2 templates.\n\n## How does it work?\n\nThere's 3 pieces to this extension:\n\n1. It adds a custom Flask CLI command to your project. When you run this\n   command it looks at your static files and then generates an md5 tagged\n   version of each file along with optionally compressing them with gzip\n   and / or brotli.\n\n2. When the above command finishes it creates a `cache_manifest.json` file in\n   your static folder which maps the regular file names, such as\n   `images/flask.png` to `images/flask-f86b271a51b3cfad5faa9299dacd987f.png`.\n\n3. It adds a new template helper called `static_url_for` which uses Flask's\n   `url_for` under the hood but is aware of the `cache_manifest.json` file so\n   it knows how to resolve `images/flask.png` to the md5 tagged file name.\n\n### Demo video\n\nThis 25 minute video goes over using this extension but it also spends a lot\nof time on the \"why\" where we cover topics like cache busting and why IMO you\nmight want to use this extension in all of your Flask projects.\n\nIf you prefer reading instead of video, this README file covers installing,\nconfiguring and using this extension too.\n\n[![Demo\nVideo](https://img.youtube.com/vi/-Xd84hlIjkI/0.jpg)](https://www.youtube.com/watch?v=-Xd84hlIjkI)\n\n#### Changes since this video\n\n- `FLASK_STATIC_DIGEST_HOST_URL` has been added to configure an optional external host, aka. CDN ([explained here](#configuring-this-extension))\n- If your blueprints have static files they will get digested now too (including nested blueprints!)\n- Optional Brotli support has been added\n- `FLASK_STATIC_DIGEST_COMPRESSION` has been added to control compression ([explained here](#configuring-this-extension))\n\n## Table of Contents\n\n- [Installation](#installation)\n- [Using the newly added Flask CLI command](#using-the-newly-added-flask-cli-command)\n- [Going over the Flask CLI commands](#going-over-the-flask-cli-commands)\n- [Configuring this extension](#configuring-this-extension)\n- [Modifying your templates to use static_url_for instead of url_for](#modifying-your-templates-to-use-static_url_for-instead-of-url_for)\n- [Potentially updating your .gitignore file](#potentially-updating-your-gitignore-file)\n- [FAQ](#faq)\n  - [What about development vs production and performance implications?](#what-about-development-vs-production-and-performance-implications)\n  - [Why bother compressing your static files here instead of with nginx?](#why-bother-compressing-your-static-files-here-instead-of-with-nginx)\n  - [How do you use this extension with Webpack or another build tool?](#how-do-you-use-this-extension-with-webpack-or-another-build-tool)\n  - [Migrating from Flask-Webpack](#migrating-from-flask-webpack)\n  - [How do you use this extension with Docker?](#how-do-you-use-this-extension-with-docker)\n  - [How do you use this extension with Heroku?](#how-do-you-use-this-extension-with-heroku)\n  - [What about user uploaded files?](#what-about-user-uploaded-files)\n- [About the author](#about-the-author)\n\n## Installation\n\n*You'll need to be running Python 3.6+ and using Flask 1.0 or greater.*\n\n`pip install Flask-Static-Digest`\n\nTo install with Brotli support:\n\n`pip install Flask-Static-Digest[brotli]`\n\n### Example directory structure for a 'hello' app\n\n```\n├── hello\n│   ├── __init__.py\n│   ├── app.py\n│   └── static\n│       └── css\n│           ├── app.css\n└── requirements.txt\n```\n\n### Flask app factory example using this extension\n\n```py\nfrom flask import Flask\nfrom flask_static_digest import FlaskStaticDigest\n\nflask_static_digest = FlaskStaticDigest()\n\n\ndef create_app():\n    app = Flask(__name__)\n\n    flask_static_digest.init_app(app)\n\n    @app.route(\"/\")\n    def index():\n        return \"Hello, World!\"\n\n    return app\n```\n\n*A more complete example app can be found in the [tests/\ndirectory](https://github.com/nickjj/flask-static-digest/tree/master/tests/example_app).*\n\n## Using the newly added Flask CLI command\n\nYou'll want to make sure to at least set the `FLASK_APP` environment variable:\n\n```sh\nexport FLASK_APP=hello.app\nexport FLASK_ENV=development\n```\n\nThen run the `flask` binary to see its help menu:\n\n```sh\nUsage: flask [OPTIONS] COMMAND [ARGS]...\n\n  ...\n\nOptions:\n  --version  Show the flask version\n  --help     Show this message and exit.\n\nCommands:\n  digest  md5 tag and compress static files.\n  routes  Show the routes for the app.\n  run     Run a development server.\n  shell   Run a shell in the app context.\n```\n\nIf all went as planned you should see the new `digest` command added to the\nlist of commands.\n\n## Going over the Flask CLI commands\n\nRunning `flask digest` will produce this help menu:\n\n```sh\nUsage: flask digest [OPTIONS] COMMAND [ARGS]...\n\n  md5 tag and compress static files.\n\nOptions:\n  --help  Show this message and exit.\n\nCommands:\n  clean    Remove generated static files and cache manifest.\n  compile  Generate optimized static files and a cache manifest.\n```\n\nEach command is labeled, but here's a bit more information on what they do.\n\n### compile\n\nInspects your Flask app's and blueprint's `static_folder` and uses that as both\nthe input and output path of where to look for and create the newly digested\nand compressed files.\n\nAt a high level it recursively loops over all of the files it finds in that\ndirectory and then generates the md5 tagged and compressed versions of each\nfile. It also creates a `cache_manifest.json` file in the root of your\n`static_folder`.\n\nThat manifest file is machine generated meaning you should not edit it unless\nyou really know what you're doing.\n\nThis file maps the human readable file name of let's say `images/flask.png` to\nthe digested file name. It's a simple key / value set up. It's basically a\nPython dictionary in JSON format.\n\nIn the end it means if your static folder looked like this originally:\n\n- `css/app.css`\n- `js/app.js`\n- `images/flask.png`\n\nAnd you decided to run the compile command, it would now look like this:\n\n- `css/app.css`\n- `css/app.css.gz`\n- `css/app-5d41402abc4b2a76b9719d911017c592.css`\n- `css/app-5d41402abc4b2a76b9719d911017c592.css.gz`\n- `js/app.js`\n- `js/app.js.gz`\n- `js/app-098f6bcd4621d373cade4e832627b4f6.js`\n- `js/app-098f6bcd4621d373cade4e832627b4f6.js.gz`\n- `images/flask.png`\n- `images/flask.png.gz`\n- `images/flask-f86b271a51b3cfad5faa9299dacd987f.png`\n- `images/flask-f86b271a51b3cfad5faa9299dacd987f.png.gz`\n- `cache_manifest.json`\n\n*Your md5 hashes will be different because it depends on what the contents of\nthe file are.*\n\n### clean\n\nInspects your Flask app's and blueprint's `static_folder` and uses that as the\ninput path of where to look for digested and compressed files.\n\nIt will recursively delete files that have a file extension of `.gz` or `.br`\nand files that have been digested. It determines if a file has been digested\nbased on its file name. In other words, it will delete files that match this\nregexp `r\"-[a-f\\d]{32}\"`.\n\nIn the end that means if you had these 6 files in your static folder:\n\n- `images/flask.png`\n- `images/flask.png.gz`\n- `images/flask.png.br`\n- `images/flask-f86b271a51b3cfad5faa9299dacd987f.png`\n- `images/flask-f86b271a51b3cfad5faa9299dacd987f.png.gz`\n- `images/flask-f86b271a51b3cfad5faa9299dacd987f.png.br`\n\nAnd you decided to run the clean command, the last 5 files would be deleted\nleaving you with the original `images/flask.png`.\n\n## Configuring this extension\n\nBy default this extension will create md5 tagged versions of all files it finds\nin your configured `static_folder`. It will also create gzip'ed versions of each\nfile and it won't prefix your static files with an external host.\n\nIf you don't like any of this behavior or you wish to enable brotli you can\noptionally configure:\n\n```py\nFLASK_STATIC_DIGEST_BLACKLIST_FILTER = []\n# If you want specific extensions to not get md5 tagged you can add them to\n# the list, such as: [\".htm\", \".html\", \".txt\"]. Make sure to include the \".\".\n\nFLASK_STATIC_DIGEST_COMPRESSION = [\"gzip\"]\n# Optionally compress your static files, supported values are:\n#   []                 avoids any compression\n#   [\"gzip\"]           uses gzip\n#   [\"brotli\"]         uses brotli (prefer either gzip or both)\n#   [\"gzip\", \"brotli\"] uses both\n\nFLASK_STATIC_DIGEST_HOST_URL = None\n# When set to a value such as https://cdn.example.com and you use static_url_for\n# it will prefix your static path with this URL. This would be useful if you\n# host your files from a CDN. Make sure to include the protocol (aka. https://).\n```\n\nYou can override these defaults in your Flask app's config file.\n\n## Modifying your templates to use static_url_for instead of url_for\n\nWe're all familiar with this code right?\n\n```html\n\u003cimg src=\"{{ url_for('static', filename='images/flask.png') }}\"\n     width=\"480\" height=\"188\" alt=\"Flask logo\" /\u003e\n```\n\nWhen you put the above code into a Flask powered Jinja 2 template, it turns\ninto this:\n\n```html\n\u003cimg src=\"images/flask.png\"\n     width=\"480\" height=\"188\" alt=\"Flask logo\" /\u003e\n```\n\nThe path might vary depending on how you configured your Flask app's\n`static_folder` but you get the idea.\n\n#### Using static_url_for instead of url_for\n\nLet's use the same example as above:\n\n```html\n\u003cimg src=\"{{ static_url_for('static', filename='images/flask.png') }}\"\n     width=\"480\" height=\"188\" alt=\"Flask logo\" /\u003e\n```\n\nBut now take a look at the output this produces:\n\n```html\n\u003cimg src=\"/images/flask-f86b271a51b3cfad5faa9299dacd987f.png\"\n     width=\"480\" height=\"188\" alt=\"Flask logo\" /\u003e\n```\n\nOr if you set `FLASK_STATIC_DIGEST_HOST_URL = \"https://cdn.example.com\"` it\nwould produce:\n\n```html\n\u003cimg src=\"https://cdn.example.com/images/flask-f86b271a51b3cfad5faa9299dacd987f.png\"\n     width=\"480\" height=\"188\" alt=\"Flask logo\" /\u003e\n```\n\nInstead of using `url_for` you would use `static_url_for`. This uses Flask's\n`url_for` under the hood so things like `_external=True` and everything else\n`url_for` supports is available to use with `static_url_for`.\n\nThat means to use this extension you don't have to do anything other than\ninstall it, optionally run the CLI command to generate the manifest and then\nrename your static file references to use `static_url_for` instead of\n`url_for`.\n\nIf your editor supports performing a find / replace across multiple files you\ncan quickly make the change by finding `url_for('static'` and replacing that\nwith `static_url_for('static'`. If you happen to use double quotes instead of\nsingle quotes you'll want to adjust for that too.\n\n## Potentially updating your .gitignore file\n\nIf you're using something like Webpack then chances are you're already git\nignoring the static files it produces as output. It's a common pattern to\ncommit your Webpack source static files but ignore the compiled static files\nit produces.\n\nBut if you're not using Webpack or another asset build tool then the static\nfiles that are a part of your project might have the same source and\ndestination directory. If that's the case, chances are you'll want to git\nignore the md5 tagged files as well as the compressed and `cache_manifest.json`\nfiles from version control.\n\nFor clarity, you want to ignore them because you'll be generating them on your\nserver at deploy time or within a Docker image if you're using Docker.  They\ndon't need to be tracked in version control.\n\nAdd this to your `.gitignore` file to ignore certain files this extension\ncreates:\n\n```\n*-[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f].*\n*.gz\ncache_manifest.json\n```\n\nThis allows your original static files but ignores everything else this\nextension creates. I am aware at how ridiculous that ignore rule is for the md5\nhash but using `[0-9a-f]{32}` does not work. If you know of a better way,\nplease open a PR!\n\n## FAQ\n\n### What about development vs production and performance implications?\n\nYou would typically only run the CLI command to prepare your static files for\nproduction. Running `flask digest compile` would become a part of your build\nprocess -- typically after you pip install your dependencies.\n\nIn development when the `cache_manifest.json` likely doesn't exist\n`static_url_for` calls `url_for` directly. This allows the `static_url_for`\nhelper to work in both development and production without any fuss.\n\nIt's also worth pointing out the CLI command is expected to be run before you\neven start your Flask server (or gunicorn / etc.), so there's no perceivable\nrun time performance hit. It only involves doing 1 extra dictionary lookup at\nrun time which is many orders of magnitude faster than even the most simple\ndatabase query.\n\n**In other words, this extension is not going to negatively impact the\nperformance of your web application. If anything it's going to speed it up and\nsave you money on hosting**.\n\nThat's because compressed files can be upwards of 5-10x smaller so there's less\nbytes to transfer over the network.\n\nAlso with md5 tagging each file it means you can configure your web server such\nas nginx to cache each file forever. That means if a user visits your site a\nsecond time in the future, nginx will be smart enough to load it from their\nlocal browser's cache without even contacting your server. It's a 100% local\nlook up.\n\nThis is as efficient as it gets. You can't do this normally without md5 tagging\neach file because if the file changes in the future, nginx will continue\nserving the old file until the cache expires so users will never see your\nupdates. But due to how md5 hashing works, if the contents of a file changes it\nwill get generated with a new name and nginx will serve the uncached new file.\n\nThis tactic is commonly referred to as \"cache busting\" and it's a very good\nidea to do this in production. You can even go 1 step further and serve your\nstatic files using a CDN. Using this cache busting strategy makes configuring\nyour CDN a piece of cake since you don't need to worry about ever expiring your\ncache manually.\n\n### Why bother compressing your static files here instead of with nginx?\n\nYou would still be using nginx's gzip / brotli features, but now instead of\nnginx having to compress your files on the fly at run time you can configure\nnginx to use the pre-made compressed files that this extension creates.\n\nThis way you can benefit from having maximum compression without having nginx\nwaste precious CPU cycles compressing files on the fly. This gives you the best\nof both worlds -- the highest compression ratio with no noticeable run time\nperformance penalty.\n\n### How do you use this extension with Webpack or another build tool?\n\nIt works out of the box with no extra configuration or plugins needed for\nWebpack or your build tool of choice.\n\nTypically the Webpack (or another build tool) work flow would look like this:\n\n- You configure Webpack with your source static files directory\n- You configure Webpack with your destination static files directory\n- Webpack processes your files in the source directory and copies them to the\n  destination directory\n- Flask is configured to serve static files from that destination directory\n\nFor example, your source directory might be `assets/` inside of your project\nand the destination might be `myapp/static`.\n\nThis extension will look at your Flask configuration for the `static_folder`\nand determine it's set to `myapp/static` so it will md5 tag and compress those\nfiles. Your Webpack source files will not get digested and compressed.\n\n### Migrating from Flask-Webpack\n\n[Flask-Webpack](https://github.com/nickjj/flask-webpack) is another extension I\nwrote a long time ago which was specific to Webpack but had a similar idea to\nthis extension. Flask-Webpack is now deprecated in favor of\nFlask-Static-Digest. Migrating is fairly painless. There are a number of\nchanges but on the bright side you get to delete more code than you add!\n\n#### Dependency / Flask app changes\n\n- Remove `Flask-Webpack` from `requirements.txt`\n- Remove all references to Flask-Webpack from your Flask app and config\n- Remove `manifest-revision-webpack-plugin` from `package.json`\n- Remove all references to this webpack plugin from your webpack config\n- Add `Flask-Static-Digest` to `requirements.txt`\n- Add the Flask-Static-Digest extension to your Flask app\n\n#### Jinja 2 template changes\n\n- Replace `stylesheet_tag('main_css') | safe` with `static_url_for('static', filename='css/main.css')`\n- Replace `javascript_tag('main_js') | safe` with `static_url_for('static', filename='js/main.js')`\n- Replace any occurrences of `asset_url_for('foo.png')` with `static_url_for('static', filename='images/foo.png')`\n\n### How do you use this extension with Docker?\n\nIt's really no different than without Docker, but instead of running `flask\ndigest compile` on your server directly at deploy time you would run it inside\nof your Docker image at build time. This way your static files are already set\nup and ready to go by the time you pull and use your Docker image in\nproduction.\n\nYou can see a fully working example of this in the open source version of my\n[Build a SAAS App with\nFlask](https://github.com/nickjj/build-a-saas-app-with-flask) course. It\nleverages Docker's build arguments to only compile the static files when\n`FLASK_ENV` is set to `production`. The key files to look at are the\n`Dockerfile`, `docker-compose.yml` and `.env` files. That wires up the build\narguments and env variables to make it work.\n\n### How do you use this extension with Heroku?\n\nIf you're deploying to Heroku using the Python buildpack you can follow these 2 steps:\n\n1. Create a `bin/post_compile` file in your project's source code\n2. Copy the lines below into the `bin/post_compile` file, save it and commit the changes\n\n```sh\n#!/usr/bin/env bash\n\nset -e\n\necho \"-----\u003e Digesting static files\"\ncd \"${1}\" \u0026\u0026 flask digest compile\n```\n\nThe next time you push your code this script will run after your pip\ndependencies are installed. It will run before your slug is compiled which\nensures that the digested files are available before any traffic is served to\nyour Dyno.\n\nYou can view how this file gets executed by Heroku in their [Python buildpack's\nsource\ncode](https://github.com/heroku/heroku-buildpack-python/blob/main/bin/steps/hooks/post_compile).\n\n### What about user uploaded files?\n\nLet's say that besides having static files like your logo and CSS / JavaScript\nbundles you also have files uploaded by users. This could be things like a user\navatar, blog post images or anything else.\n\n**You would still want to md5 tag and compress these files but now we've run\ninto a situation**. The `flask digest compile` command is meant to be run at\ndeploy time and it could potentially be run from your dev box, inside of a\nDocker image, on a CI server or your production server. In these cases you\nwouldn't have access to the user uploaded files.\n\nBut at the same time you have users uploading files at run time. They are\nchanging all the time.\n\n**Needless to say you can't use the `flask digest compile` command to digest\nuser uploaded files**. The `cache_manifest.json` file should be reserved for\nfiles that exist in your code repo (such as your CSS / JS bundles, maybe a\nlogo, fonts, etc.).\n\nThe above files do not change at run time and align well with running the\n`flask digest compile` command at deploy time.\n\nFor user uploaded content you wouldn't ever write these entries to the manifest\nJSON file. Instead, you would typically upload your files to disk, S3 or\nsomewhere else and then save the file name of the file you uploaded into your\nlocal database.\n\nSo now when you reference a user uploaded file (let's say an avatar), you would\nloop over your users from the database and reference the file name from the DB.\n\nThere's no need for a manifest file to store the user uploaded files because\nthe database has a reference to the real name and then you are dynamically\nreferencing that in your template helper (`static_url_for`), so it's never a\nhard coded thing that changes at the template level.\n\nWhat's cool about this is you already did the database query to retrieve the\nrecord(s) from the database, so there's no extra database work to do. All you\nhave to do is reference the file name field that's a part of your model.\n\nBut that doesn't fully solve the problem. You'll still want to md5 tag and\ncompress your user uploaded content at run time and you would want to do this\nbefore you save the uploaded file into its final destination (local file system,\nS3, etc.).\n\nThis can be done completely separate from this extension and it's really going\nto vary depending on where you host your user uploaded content. For example some\nCDNs will automatically create compressed files for you and they use things like\nan ETag header in the response to include a unique file name (and this is what\nyou can store in your DB).\n\nSo maybe md5 hashing and maybe compressing your user uploaded content becomes an\napp specific responsibility, although I'm not opposed to maybe creating helper\nfunctions you can use but that would need to be thought out carefully.\n\nHowever the implementation is not bad. It's really only about 5 lines of code\nto do both things. Feel free to `CTRL + F` around the [code\nbase](https://github.com/nickjj/flask-static-digest/blob/master/flask_static_digest/digester.py)\nfor `hashlib`, `gzip` and `brotli` and you'll find the related code.\n\n**So with that said, here's a work flow you can do to deal with this today:**\n\n- User uploads file\n- Your Flask app potentially md5 tags / gzips the file if necessary\n- Your Flask app saves the file name + compressed file to its final destination (local file system, S3, etc.)\n- Your Flask app saves the final unique file name to your database\n\nThat final unique file name would be the md5 tagged version of the file that\nyou created or the unique file name that your CDN returned back to you. I hope\nthat clears up how to deal with user uploaded files and efficiently serving\nthem!\n\n## About the author\n\n- Nick Janetakis | \u003chttps://nickjanetakis.com\u003e | [@nickjanetakis](https://twitter.com/nickjanetakis)\n\nIf you're interested in learning Flask I have a 17+ hour video course called\n[Build a SAAS App with\nFlask](https://buildasaasappwithflask.com/?utm_source=github\u0026utm_medium=staticdigest\u0026utm_campaign=readme).\nIt's a course where we build a real world SAAS app. Everything about the course\nand demo videos of what we build is on the site linked above.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickjj%2Fflask-static-digest","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnickjj%2Fflask-static-digest","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnickjj%2Fflask-static-digest/lists"}