flask项目完成,部署到apache时遇到了问题。部分静态文件的content-type没有甚至出现了乱码,浏览器报错content-type mismatch。这是因为链接资源的时候,资源类型已经被标注,比如text/css
?然而在http响应头里面却变成其他类型。
对于CSS文件,如果HTTP头里面的content-type标注出错,这会导致浏览器样式表不能够被正确加载。flask内部使用一个函数send_file
来返回对静态资源的请求。仔细检查这一函数, flask利用文件名来推测文件的类型,对于猜测出来的多个文件类型,flask会选择第一个作为mimetype。问题出现在什么地方现在并不确定。但是使用apache伺服静态资源可以暂时解决问题。
附源代码
def send_file(filename_or_fp, mimetype=None, as_attachment=False, attachment_filename=None, add_etags=True, cache_timeout=None, conditional=False): """Sends the contents of a file to the client. This will use the most efficient method available and configured. By default it will try to use the WSGI server's file_wrapper support. Alternatively you can set the application's :attr:`~Flask.use_x_sendfile` attribute to ``True`` to directly emit an `X-Sendfile` header. This however requires support of the underlying webserver for `X-Sendfile`. By default it will try to guess the mimetype for you, but you can also explicitly provide one. For extra security you probably want to send certain files as attachment (HTML for instance). The mimetype guessing requires a `filename` or an `attachment_filename` to be provided. Please never pass filenames to this function from user sources without checking them first. Something like this is usually sufficient to avoid security problems:: if '..' in filename or filename.startswith('/'): abort(404) .. versionadded:: 0.2 .. versionadded:: 0.5 The `add_etags`, `cache_timeout` and `conditional` parameters were added. The default behavior is now to attach etags. .. versionchanged:: 0.7 mimetype guessing and etag support for file objects was deprecated because it was unreliable. Pass a filename if you are able to, otherwise attach an etag yourself. This functionality will be removed in Flask 1.0 .. versionchanged:: 0.9 cache_timeout pulls its default from application config, when None. :param filename_or_fp: the filename of the file to send. This is relative to the :attr:`~Flask.root_path` if a relative path is specified. Alternatively a file object might be provided in which case `X-Sendfile` might not work and fall back to the traditional method. Make sure that the file pointer is positioned at the start of data to send before calling :func:`send_file`. :param mimetype: the mimetype of the file if provided, otherwise auto detection happens. :param as_attachment: set to `True` if you want to send this file with a ``Content-Disposition: attachment`` header. :param attachment_filename: the filename for the attachment if it differs from the file's filename. :param add_etags: set to `False` to disable attaching of etags. :param conditional: set to `True` to enable conditional responses. :param cache_timeout: the timeout in seconds for the headers. When `None` (default), this value is set by :meth:`~Flask.get_send_file_max_age` of :data:`~flask.current_app`. """ mtime = None if isinstance(filename_or_fp, string_types): filename = filename_or_fp file = None else: from warnings import warn file = filename_or_fp filename = getattr(file, 'name', None) # XXX: this behavior is now deprecated because it was unreliable. # removed in Flask 1.0 if not attachment_filename and not mimetype \ and isinstance(filename, string_types): warn(DeprecationWarning('The filename support for file objects ' 'passed to send_file is now deprecated. Pass an ' 'attach_filename if you want mimetypes to be guessed.'), stacklevel=2) if add_etags: warn(DeprecationWarning('In future flask releases etags will no ' 'longer be generated for file objects passed to the send_file ' 'function because this behavior was unreliable. Pass ' 'filenames instead if possible, otherwise attach an etag ' 'yourself based on another value'), stacklevel=2) if filename is not None: if not os.path.isabs(filename): filename = os.path.join(current_app.root_path, filename) if mimetype is None and (filename or attachment_filename): mimetype = mimetypes.guess_type(filename or attachment_filename)[0] if mimetype is None: mimetype = 'application/octet-stream' headers = Headers() if as_attachment: if attachment_filename is None: if filename is None: raise TypeError('filename unavailable, required for ' 'sending as attachment') attachment_filename = os.path.basename(filename) headers.add('Content-Disposition', 'attachment', filename=attachment_filename) if current_app.use_x_sendfile and filename: if file is not None: file.close() headers['X-Sendfile'] = filename headers['Content-Length'] = os.path.getsize(filename) data = None else: if file is None: file = open(filename, 'rb') mtime = os.path.getmtime(filename) headers['Content-Length'] = os.path.getsize(filename) data = wrap_file(request.environ, file) rv = current_app.response_class(data, mimetype=mimetype, headers=headers, direct_passthrough=True) # if we know the file modification date, we can store it as the # the time of the last modification. if mtime is not None: rv.last_modified = int(mtime) rv.cache_control.public = True if cache_timeout is None: cache_timeout = current_app.get_send_file_max_age(filename) if cache_timeout is not None: rv.cache_control.max_age = cache_timeout rv.expires = int(time() + cache_timeout) if add_etags and filename is not None: rv.set_etag('flask-%s-%s-%s' % ( os.path.getmtime(filename), os.path.getsize(filename), adler32( filename.encode('utf-8') if isinstance(filename, text_type) else filename ) & 0xffffffff )) if conditional: rv = rv.make_conditional(request) # make sure we don't send x-sendfile for servers that # ignore the 304 status code for x-sendfile. if rv.status_code == 304: rv.headers.pop('x-sendfile', None) return rv
?
?
?
?
原文地址:flask静态文件部署, 感谢原作者分享。 回首往事,日子里竟全是斑澜的光影,