diff --git a/owl/__init__.py b/owl/__init__.py
index 86fde2f1181c95a79d86608b7182c2be4767352a..146b117720ce2a0cbac8755cf83e8800ce243319 100644
--- a/owl/__init__.py
+++ b/owl/__init__.py
@@ -7,6 +7,7 @@ import logging
 import os
 from sqlalchemy import select
 from sqlalchemy.orm import joinedload
+import tempfile
 import werkzeug.exceptions
 
 import owl.assets as assets
@@ -14,6 +15,35 @@ import owl.config as config
 import owl.db as db
 
 
+# We wrap Flask to make it store uploaded files in a directory of our choice.
+# This enables us to hard-link the uploaded files to their final place.
+# To achieve that, we subclass Request to make it use a subclassed FormDataParser,
+# which calls a custom stream factory.
+
+def owl_stream_factory(total_content_length, filename, content_type, content_length=None):
+    return tempfile.NamedTemporaryFile(dir=os.path.join(app.instance_path, 'tmp'), prefix='upload-')
+
+
+class FormDataParser(werkzeug.formparser.FormDataParser):
+
+    def __init__(self,
+                 stream_factory=None,
+                 charset='utf-8',
+                 errors='replace',
+                 max_form_memory_size=None,
+                 max_content_length=None,
+                 cls=None,
+                 silent=True):
+        super().__init__(owl_stream_factory, charset, errors, max_form_memory_size, max_content_length, cls, silent)
+
+
+class Request(flask.wrappers.Request):
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+        self.form_data_parser_class = FormDataParser
+
+
 class OwlFlask(Flask):
 
     """
@@ -106,6 +136,7 @@ static_dir = os.path.abspath('static')
 # Application object
 app = OwlFlask(__name__, static_folder=static_dir, instance_path=data_dir)
 app.config.from_object(config)
+app.request_class = Request
 db.flask_db = SQLAlchemy(app, metadata=db.metadata)