diff --git a/synapse/rest/media/v1/_base.py b/synapse/rest/media/v1/_base.py
index 03df875b442a99de7c85a223d96002da759f84fa..1145904aeb5227157137c94ffb6cc68105626b61 100644
--- a/synapse/rest/media/v1/_base.py
+++ b/synapse/rest/media/v1/_base.py
@@ -144,15 +144,16 @@ def respond_with_responder(request, responder, media_type, file_size, upload_nam
         return
 
     add_file_headers(request, media_type, file_size, upload_name)
-    yield responder.write_to_consumer(request)
+    with responder:
+        yield responder.write_to_consumer(request)
     finish_request(request)
 
 
 class Responder(object):
     """Represents a response that can be streamed to the requester.
 
-    Either `write_to_consumer` or `cancel` must be called to clean up any open
-    resources.
+    Responder is a context manager which *must* be used, so that any resources
+    held can be cleaned up.
     """
     def write_to_consumer(self, consumer):
         """Stream response into consumer
@@ -165,9 +166,10 @@ class Responder(object):
         """
         pass
 
-    def cancel(self):
-        """Called when the responder is not going to be used after all.
-        """
+    def __enter__(self):
+        pass
+
+    def __exit__(self, exc_type, exc_val, exc_tb):
         pass
 
 
diff --git a/synapse/rest/media/v1/media_storage.py b/synapse/rest/media/v1/media_storage.py
index 49d2b7cd45b92294d0ba4af4e2e19e59e062adfe..b6e7a19e12deb509f0dbd232a31779f1e1880f8c 100644
--- a/synapse/rest/media/v1/media_storage.py
+++ b/synapse/rest/media/v1/media_storage.py
@@ -220,8 +220,7 @@ class FileResponder(Responder):
 
     @defer.inlineCallbacks
     def write_to_consumer(self, consumer):
-        with self.open_file:
-            yield FileSender().beginFileTransfer(self.open_file, consumer)
+        yield FileSender().beginFileTransfer(self.open_file, consumer)
 
-    def cancel(self):
+    def __exit__(self, exc_type, exc_val, exc_tb):
         self.open_file.close()