Open a file in a safe way and return :exc:`HTTPResponse` with status code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``, ``Content-Length`` and ``Last-Modified`` headers are set if possible. Special support for ``If-Modified-Since``, ``Range`` and ``HEAD``
(filename, root, mimetype='auto', download=False, charset='UTF-8')
| 2451 | |
| 2452 | |
| 2453 | def static_file(filename, root, mimetype='auto', download=False, charset='UTF-8'): |
| 2454 | """ Open a file in a safe way and return :exc:`HTTPResponse` with status |
| 2455 | code 200, 305, 403 or 404. The ``Content-Type``, ``Content-Encoding``, |
| 2456 | ``Content-Length`` and ``Last-Modified`` headers are set if possible. |
| 2457 | Special support for ``If-Modified-Since``, ``Range`` and ``HEAD`` |
| 2458 | requests. |
| 2459 | |
| 2460 | :param filename: Name or path of the file to send. |
| 2461 | :param root: Root path for file lookups. Should be an absolute directory |
| 2462 | path. |
| 2463 | :param mimetype: Defines the content-type header (default: guess from |
| 2464 | file extension) |
| 2465 | :param download: If True, ask the browser to open a `Save as...` dialog |
| 2466 | instead of opening the file with the associated program. You can |
| 2467 | specify a custom filename as a string. If not specified, the |
| 2468 | original filename is used (default: False). |
| 2469 | :param charset: The charset to use for files with a ``text/*`` |
| 2470 | mime-type. (default: UTF-8) |
| 2471 | """ |
| 2472 | |
| 2473 | root = os.path.abspath(root) + os.sep |
| 2474 | filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) |
| 2475 | headers = dict() |
| 2476 | |
| 2477 | if not filename.startswith(root): |
| 2478 | return HTTPError(403, "Access denied.") |
| 2479 | if not os.path.exists(filename) or not os.path.isfile(filename): |
| 2480 | return HTTPError(404, "File does not exist.") |
| 2481 | if not os.access(filename, os.R_OK): |
| 2482 | return HTTPError(403, "You do not have permission to access this file.") |
| 2483 | |
| 2484 | if mimetype == 'auto': |
| 2485 | mimetype, encoding = mimetypes.guess_type(filename) |
| 2486 | if encoding: headers['Content-Encoding'] = encoding |
| 2487 | |
| 2488 | if mimetype: |
| 2489 | if mimetype[:5] == 'text/' and charset and 'charset' not in mimetype: |
| 2490 | mimetype += '; charset=%s' % charset |
| 2491 | headers['Content-Type'] = mimetype |
| 2492 | |
| 2493 | if download: |
| 2494 | download = os.path.basename(filename if download == True else download) |
| 2495 | headers['Content-Disposition'] = 'attachment; filename="%s"' % download |
| 2496 | |
| 2497 | stats = os.stat(filename) |
| 2498 | headers['Content-Length'] = clen = stats.st_size |
| 2499 | lm = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime(stats.st_mtime)) |
| 2500 | headers['Last-Modified'] = lm |
| 2501 | |
| 2502 | ims = request.environ.get('HTTP_IF_MODIFIED_SINCE') |
| 2503 | if ims: |
| 2504 | ims = parse_date(ims.split(";")[0].strip()) |
| 2505 | if ims is not None and ims >= int(stats.st_mtime): |
| 2506 | headers['Date'] = time.strftime("%a, %d %b %Y %H:%M:%S GMT", time.gmtime()) |
| 2507 | return HTTPResponse(status=304, **headers) |
| 2508 | |
| 2509 | body = '' if request.method == 'HEAD' else open(filename, 'rb') |
| 2510 |
no test coverage detected