hs: Make the uploads directory if it doesn't exist. Namespace uploads by the base64 encoded user id of the uploader. Make a reasonable attempt to retry clashing upload paths. Try to guess a sensible file extension depending on the content type.

This commit is contained in:
Kegan Dougal 2014-08-18 16:18:11 +01:00
parent 35da1bf4a3
commit 590ab24c85
1 changed files with 46 additions and 5 deletions

View File

@ -17,16 +17,19 @@
from syutil.jsonutil import ( from syutil.jsonutil import (
encode_canonical_json, encode_pretty_printed_json encode_canonical_json, encode_pretty_printed_json
) )
from synapse.api.errors import cs_exception, CodeMessageException from synapse.api.errors import cs_exception, SynapseError, CodeMessageException
from synapse.util.stringutils import random_string
from twisted.internet import defer, reactor from twisted.internet import defer, reactor
from twisted.web import server, resource from twisted.web import server, resource
from twisted.web.server import NOT_DONE_YET from twisted.web.server import NOT_DONE_YET
from twisted.web.util import redirectTo from twisted.web.util import redirectTo
import base64
import collections import collections
import json import json
import logging import logging
import os
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
@ -188,14 +191,52 @@ class FileUploadResource(resource.Resource):
file_map_func = self.map_request_to_name file_map_func = self.map_request_to_name
self.get_name_for_request = file_map_func self.get_name_for_request = file_map_func
if not os.path.isdir(self.directory):
os.mkdir(self.directory)
logger.info("FileUploadResource : Created %s directory.",
self.directory)
@defer.inlineCallbacks @defer.inlineCallbacks
def map_request_to_name(self, request): def map_request_to_name(self, request):
# auth the user # auth the user
auth_user = yield self.auth.get_user_by_req(request) auth_user = yield self.auth.get_user_by_req(request)
logger.info("User %s is uploading a file.", auth_user)
defer.returnValue("boo2.png")
def render(self, request): # namespace all file uploads on the user
prefix = base64.urlsafe_b64encode(
auth_user.to_string()
).replace('=', '')
# use a random string for the main portion
main_part = random_string(24)
# suffix with a file extension if we can make one. This is nice to
# provide a hint to clients on the file information.
suffix = ""
if request.requestHeaders.hasHeader("Content-Type"):
content_type = request.requestHeaders.getRawHeaders(
"Content-Type")[0]
if (content_type.split("/")[0].lower() in
["image", "video", "audio"]):
suffix = "." + content_type.split("/")[-1]
file_path = os.path.join(self.directory, prefix + main_part + suffix)
logger.info("User %s is uploading a file to path %s",
auth_user.to_string(),
file_path)
# keep trying to make a non-clashing file, with a sensible max attempts
attempts = 0
while os.path.exists(file_path):
main_part = random_string(24)
file_path = os.path.join(self.directory,
prefix + main_part + suffix)
attempts += 1
if attempts > 25: # really? Really?
raise SynapseError(500, "Unable to create file.")
defer.returnValue(file_path)
def render_POST(self, request):
self._async_render(request) self._async_render(request)
return server.NOT_DONE_YET return server.NOT_DONE_YET
@ -208,7 +249,7 @@ class FileUploadResource(resource.Resource):
f.write(request.content.read()) f.write(request.content.read())
respond_with_json_bytes(request, 200, respond_with_json_bytes(request, 200,
json.dumps({"url": "not_implemented2"}), json.dumps({"path": fname}),
send_cors=True) send_cors=True)
except CodeMessageException as e: except CodeMessageException as e: