def serve(self, id, download=False, **kwargs): """Serve a :class:`~mediadrop.model.media.MediaFile` binary. :param id: File ID :type id: ``int`` :param bool download: If true, serve with an Content-Disposition that makes the file download to the users computer instead of playing in the browser. :raises webob.exc.HTTPNotFound: If no file exists with this ID. :raises webob.exc.HTTPNotAcceptable: If an Accept header field is present, and if the mimetype of the requested file doesn't match, then a 406 (not acceptable) response is returned. """ file = fetch_row(MediaFile, id=id) request.perm.assert_permission(u'view', file.media.resource) file_type = file.mimetype.encode('utf-8') file_name = file.display_name.encode('utf-8') file_path = helpers.file_path(file) if file_path is None: log.warn('No path exists for requested media file: %r', file) raise HTTPNotFound() file_path = file_path.encode('utf-8') if not os.path.exists(file_path): log.warn('No such file or directory: %r', file_path) raise HTTPNotFound() # Ensure the request accepts files with this container accept = request.environ.get('HTTP_ACCEPT', '*/*') if not mimeparse.best_match([file_type], accept): raise HTTPNotAcceptable() # 406 method = config.get('file_serve_method', None) headers = [] # Serving files with this header breaks playback on iPhone if download: headers.append(('Content-Disposition', 'attachment; filename="%s"' % file_name)) if method == 'apache_xsendfile': # Requires mod_xsendfile for Apache 2.x # XXX: Don't send Content-Length or Etag headers, # Apache handles them for you. response.headers['X-Sendfile'] = file_path response.body = '' elif method == 'nginx_redirect': # Requires NGINX server configuration: # NGINX must have a location block configured that matches # the /__mediadrop_serve__ path below (the value configured by # setting "nginx_serve_path" option in the configuration). It # should also be configured as an "internal" location to prevent # people from surfing directly to it. # For more information see: http://wiki.nginx.org/XSendfile serve_path = config.get('nginx_serve_path', '__mediadrop_serve__') if not serve_path.startswith('/'): serve_path = '/' + serve_path redirect_filename = '%s/%s' % (serve_path, os.path.basename(file_path)) response.headers['X-Accel-Redirect'] = redirect_filename else: app = FileApp(file_path, headers, content_type=file_type) return forward(app) response.headers['Content-Type'] = file_type for header, value in headers: response.headers[header] = value return None