bottle_custom.py 3.0 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970
  1. # Custom static_file stuff
  2. # Forked form bottle.py
  3. import os
  4. import bottle as b
  5. import time
  6. def custom_static_file(filename, root, request=None, custom_headers=None, mimetype='auto', download=False, charset='UTF-8'):
  7. """ Open a file in a safe way and return :exc:`HTTPResponse` with status
  8. code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``,
  9. ``Content-Length`` and ``Last-Modified`` headers are set if possible.
  10. Special support for ``If-Modified-Since``, ``Range`` and ``HEAD``
  11. requests.
  12. :param filename: Name or path of the file to send.
  13. :param root: Root path for file lookups. Should be an absolute directory
  14. path.
  15. :param mimetype: Defines the content-type header (default: guess from
  16. file extension)
  17. :param download: If True, ask the browser to open a `Save as...` dialog
  18. instead of opening the file with the associated program. You can
  19. specify a custom filename as a string. If not specified, the
  20. original filename is used (default: False).
  21. :param charset: The charset to use for files with a ``text/*``
  22. mime-type. (default: UTF-8)
  23. """
  24. root = os.path.abspath(root) + os.sep
  25. filename = os.path.abspath(os.path.join(root, filename.strip('/\\')))
  26. headers = dict()
  27. for key in custom_headers.keys():
  28. headers[key] = custom_headers[key]
  29. if not filename.startswith(root):
  30. return b.HTTPError(403, "Access denied.")
  31. if not os.path.exists(filename) or not os.path.isfile(filename):
  32. return b.HTTPError(404, "File does not exist.")
  33. if not os.access(filename, os.R_OK):
  34. return b.HTTPError(403, "You do not have permission to access this file.")
  35. if mimetype:
  36. if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype:
  37. mimetype += '; charset=%s' % charset
  38. headers['Content-Type'] = mimetype
  39. if download:
  40. download = os.path.basename(filename if download is True else download)
  41. headers['Content-Disposition'] = 'attachment; filename="%s"' % download
  42. stats = os.stat(filename)
  43. headers['Content-Length'] = stats.st_size # `clen` was here, but I'm not using it
  44. lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime))
  45. headers['Last-Modified'] = lm
  46. body = '' if request.method == 'HEAD' else open(filename, 'rb')
  47. headers["Accept-Ranges"] = "bytes"
  48. #if 'HTTP_RANGE' in request.environ:
  49. # ranges = list(parse_range_header(request.environ['HTTP_RANGE'], clen))
  50. # if not ranges:
  51. # return HTTPError(416, "Requested Range Not Satisfiable")
  52. # offset, end = ranges[0]
  53. # headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end-1, clen)
  54. # headers["Content-Length"] = str(end-offset)
  55. # if body: body = _file_iter_range(body, offset, end-offset)
  56. # return HTTPResponse(body, status=206, **headers)
  57. return b.HTTPResponse(body, **headers)