Ejemplo n.º 1
0
    async def download_folder_as_zip(self):
        zipfile_name = self.path.name or '{}-archive'.format(self.provider.NAME)
        self.set_header('Content-Type', 'application/zip')
        self.set_header('Content-Disposition', make_disposition(zipfile_name + '.zip'))

        result = await self.provider.zip(self.path)

        await self.write_stream(result)
Ejemplo n.º 2
0
    async def download_folder_as_zip(self):
        zipfile_name = self.path.name or '{}-archive'.format(
            self.provider.NAME)
        self.set_header('Content-Type', 'application/zip')
        self.set_header('Content-Disposition',
                        make_disposition(zipfile_name + '.zip'))

        result = await self.provider.zip(self.path)

        await self.write_stream(result)
Ejemplo n.º 3
0
    async def download_file(self):
        if 'Range' not in self.request.headers:
            request_range = None
        else:
            logger.debug('Range header is: {}'.format(self.request.headers['Range']))
            request_range = utils.parse_request_range(self.request.headers['Range'])
            logger.debug('Range header parsed as: {}'.format(request_range))

        version = self.requested_version
        stream = await self.provider.download(
            self.path,
            revision=version,
            range=request_range,
            accept_url='direct' not in self.request.query_arguments,
            mode=self.get_query_argument('mode', default=None),
            display_name=self.get_query_argument('displayName', default=None),
        )

        if isinstance(stream, str):
            return self.redirect(stream)

        if getattr(stream, 'partial', None):
            # Use getattr here as not all stream may have a partial attribute
            # Plus it fixes tests
            self.set_status(206)
            self.set_header('Content-Range', stream.content_range)

        if stream.content_type is not None:
            self.set_header('Content-Type', stream.content_type)

        logger.debug('stream size is: {}'.format(stream.size))
        if stream.size is not None:
            self.set_header('Content-Length', str(stream.size))

        # Build `Content-Disposition` header from `displayName` override,
        # headers of provider response, or file path, whichever is truthy first
        name = (
            self.get_query_argument('displayName', default=None) or
            getattr(stream, 'name', None) or
            self.path.name
        )
        self.set_header('Content-Disposition', make_disposition(name))

        _, ext = os.path.splitext(name)
        # If the file extention is in mime_types
        # override the content type to fix issues with safari shoving in new file extensions
        if ext in mime_types:
            self.set_header('Content-Type', mime_types[ext])

        await self.write_stream(stream)

        if getattr(stream, 'partial', False) and isinstance(stream, ResponseStreamReader):
            await stream.response.release()

        logger.debug('bytes received is: {}'.format(self.bytes_downloaded))
Ejemplo n.º 4
0
    async def download_file(self):
        if 'Range' not in self.request.headers:
            request_range = None
        else:
            logger.debug('Range header is: {}'.format(
                self.request.headers['Range']))
            request_range = utils.parse_request_range(
                self.request.headers['Range'])
            logger.debug('Range header parsed as: {}'.format(request_range))

        version = self.requested_version
        stream = await self.provider.download(
            self.path,
            revision=version,
            range=request_range,
            accept_url='direct' not in self.request.query_arguments,
            mode=self.get_query_argument('mode', default=None),
            display_name=self.get_query_argument('displayName', default=None),
        )

        if isinstance(stream, str):
            return self.redirect(stream)

        if getattr(stream, 'partial', None):
            # Use getattr here as not all stream may have a partial attribute
            # Plus it fixes tests
            self.set_status(206)
            self.set_header('Content-Range', stream.content_range)

        if stream.content_type is not None:
            self.set_header('Content-Type', stream.content_type)

        logger.debug('stream size is: {}'.format(stream.size))
        if stream.size is not None:
            self.set_header('Content-Length', str(stream.size))

        # Build `Content-Disposition` header from `displayName` override,
        # headers of provider response, or file path, whichever is truthy first
        name = (self.get_query_argument('displayName', default=None)
                or getattr(stream, 'name', None) or self.path.name)
        self.set_header('Content-Disposition', make_disposition(name))

        _, ext = os.path.splitext(name)
        # If the file extention is in mime_types
        # override the content type to fix issues with safari shoving in new file extensions
        if ext in mime_types:
            self.set_header('Content-Type', mime_types[ext])

        await self.write_stream(stream)

        if getattr(stream, 'partial', False) and isinstance(
                stream, ResponseStreamReader):
            await stream.response.release()

        logger.debug('bytes received is: {}'.format(self.bytes_downloaded))
Ejemplo n.º 5
0
    async def download(
            self,
            path: WaterButlerPath,
            accept_url=False,
            range=None,  # type: ignore
            **kwargs) -> typing.Union[str, ResponseStreamReader]:
        """Download the object with the given path.


        API Docs:

            GET Object: https://cloud.google.com/storage/docs/xml-api/get-object

            Download an Object: https://cloud.google.com/storage/docs/xml-api/get-object-download

        The behavior of download differs depending on the value of ``accept_url``.  If
        ``accept_url == False``, WB makes a standard signed request and returns a
        ``ResponseStreamReader``.  If ``accept_url == True``, WB builds and signs the ``GET``
        request with an extra query parameter ``response-content-disposition`` to trigger the
        download with the display name.  The signed URL is returned.

        :param path: the WaterButlerPath for the object to download
        :type path: :class:`.WaterButlerPath`
        :param bool accept_url: should return a direct time-limited download url from the provider
        :param tuple range: the Range HTTP request header
        :param dict kwargs: ``display_name`` - the display name of the file on OSF and for download
        :rtype: str or :class:`.streams.ResponseStreamReader`
        """

        if path.is_folder:
            raise DownloadError('Cannot download folders',
                                code=HTTPStatus.BAD_REQUEST)

        req_method = 'GET'
        obj_name = utils.get_obj_name(path, is_folder=False)

        if accept_url:
            display_name = kwargs.get('display_name') or path.name
            query = {
                'response-content-disposition': make_disposition(display_name)
            }
            # There is no need to delay URL building and signing
            signed_url = self._build_and_sign_url(req_method, obj_name,
                                                  **query)  # type: ignore
            return signed_url

        signed_url = functools.partial(self._build_and_sign_url, req_method,
                                       obj_name, **{})
        resp = await self.make_request(req_method,
                                       signed_url,
                                       range=range,
                                       expects=(HTTPStatus.OK,
                                                HTTPStatus.PARTIAL_CONTENT),
                                       throws=DownloadError)
        return ResponseStreamReader(resp)
Ejemplo n.º 6
0
    async def download(self,
                       path,
                       accept_url=False,
                       revision=None,
                       range=None,
                       **kwargs):
        """Returns a ResponseWrapper (Stream) for the specified path
        raises FileNotFoundError if the status from S3 is not 200

        :param str path: Path to the key you want to download
        :param dict \*\*kwargs: Additional arguments that are ignored
        :rtype: :class:`waterbutler.core.streams.ResponseStreamReader`
        :raises: :class:`waterbutler.core.exceptions.DownloadError`
        """

        await self._check_region()

        if not path.is_file:
            raise exceptions.DownloadError('No file specified for download',
                                           code=400)

        if not revision or revision.lower() == 'latest':
            query_parameters = None
        else:
            query_parameters = {'versionId': revision}

        display_name = kwargs.get('display_name') or path.name
        response_headers = {
            'response-content-disposition': make_disposition(display_name)
        }

        url = functools.partial(self.bucket.new_key(path.path).generate_url,
                                settings.TEMP_URL_SECS,
                                query_parameters=query_parameters,
                                response_headers=response_headers)

        if accept_url:
            return url()

        resp = await self.make_request(
            'GET',
            url,
            range=range,
            expects=(
                200,
                206,
            ),
            throws=exceptions.DownloadError,
        )

        return streams.ResponseStreamReader(resp)
Ejemplo n.º 7
0
    async def get(self):
        """Download as a Zip archive."""

        zipfile_name = self.path.name or '{}-archive'.format(self.provider.NAME)
        self.set_header('Content-Type', 'application/zip')
        self.set_header(
            'Content-Disposition',
            make_disposition(zipfile_name + '.zip')
        )

        result = await self.provider.zip(**self.arguments)

        await self.write_stream(result)
        self._send_hook('download_zip', path=self.path)
Ejemplo n.º 8
0
    async def get(self):
        """Download a file."""
        try:
            self.arguments['accept_url'] = TRUTH_MAP[self.arguments.get(
                'accept_url', 'true').lower()]
        except KeyError:
            raise tornado.web.HTTPError(status_code=400)

        if 'Range' in self.request.headers:
            request_range = utils.parse_request_range(
                self.request.headers['Range'])
        else:
            request_range = None

        result = await self.provider.download(range=request_range,
                                              **self.arguments)

        if isinstance(result, str):
            self.redirect(result)
            self._send_hook('download_file', path=self.path)
            return

        if getattr(result, 'partial', None):
            # Use getattr here as not all stream may have a partial attribute
            # Plus it fixes tests
            self.set_status(206)
            self.set_header('Content-Range', result.content_range)

        if result.content_type is not None:
            self.set_header('Content-Type', result.content_type)

        if result.size is not None:
            self.set_header('Content-Length', str(result.size))

        # Build `Content-Disposition` header from `displayName` override,
        # headers of provider response, or file path, whichever is truthy first
        name = self.arguments.get('displayName') or getattr(
            result, 'name', None) or self.path.name
        self.set_header('Content-Disposition', make_disposition(name))

        _, ext = os.path.splitext(name)
        # If the file extention is in mime_types
        # override the content type to fix issues with safari shoving in new file extensions
        if ext in mime_types:
            self.set_header('Content-Type', mime_types[ext])

        await self.write_stream(result)
        self._send_hook('download_file', path=self.path)
Ejemplo n.º 9
0
    async def download(self, path: WaterButlerPath, accept_url=False, range=None,  # type: ignore
                       **kwargs) -> typing.Union[str, ResponseStreamReader]:
        """Download the object with the given path.


        API Docs:

            GET Object: https://cloud.google.com/storage/docs/xml-api/get-object

            Download an Object: https://cloud.google.com/storage/docs/xml-api/get-object-download

        The behavior of download differs depending on the value of ``accept_url``.  If
        ``accept_url == False``, WB makes a standard signed request and returns a
        ``ResponseStreamReader``.  If ``accept_url == True``, WB builds and signs the ``GET``
        request with an extra query parameter ``response-content-disposition`` to trigger the
        download with the display name.  The signed URL is returned.

        :param path: the WaterButlerPath for the object to download
        :type path: :class:`.WaterButlerPath`
        :param bool accept_url: should return a direct time-limited download url from the provider
        :param tuple range: the Range HTTP request header
        :param dict kwargs: ``display_name`` - the display name of the file on OSF and for download
        :rtype: str or :class:`.streams.ResponseStreamReader`
        """

        if path.is_folder:
            raise DownloadError('Cannot download folders', code=HTTPStatus.BAD_REQUEST)

        req_method = 'GET'
        obj_name = utils.get_obj_name(path, is_folder=False)

        if accept_url:
            display_name = kwargs.get('display_name') or path.name
            query = {'response-content-disposition': make_disposition(display_name)}
            # There is no need to delay URL building and signing
            signed_url = self._build_and_sign_url(req_method, obj_name, **query)  # type: ignore
            return signed_url

        signed_url = functools.partial(self._build_and_sign_url, req_method, obj_name, **{})
        resp = await self.make_request(
            req_method,
            signed_url,
            range=range,
            expects=(HTTPStatus.OK, HTTPStatus.PARTIAL_CONTENT),
            throws=DownloadError
        )
        return ResponseStreamReader(resp)
Ejemplo n.º 10
0
    async def get(self):
        """Download a file."""
        try:
            self.arguments['accept_url'] = TRUTH_MAP[self.arguments.get('accept_url', 'true').lower()]
        except KeyError:
            raise tornado.web.HTTPError(status_code=400)

        if 'Range' in self.request.headers:
            request_range = utils.parse_request_range(self.request.headers['Range'])
        else:
            request_range = None

        result = await self.provider.download(range=request_range, **self.arguments)

        if isinstance(result, str):
            self.redirect(result)
            self._send_hook('download_file', path=self.path)
            return

        if getattr(result, 'partial', None):
            # Use getattr here as not all stream may have a partial attribute
            # Plus it fixes tests
            self.set_status(206)
            self.set_header('Content-Range', result.content_range)

        if result.content_type is not None:
            self.set_header('Content-Type', result.content_type)

        if result.size is not None:
            self.set_header('Content-Length', str(result.size))

        # Build `Content-Disposition` header from `displayName` override,
        # headers of provider response, or file path, whichever is truthy first
        name = self.arguments.get('displayName') or getattr(result, 'name', None) or self.path.name
        self.set_header('Content-Disposition', make_disposition(name))

        _, ext = os.path.splitext(name)
        # If the file extention is in mime_types
        # override the content type to fix issues with safari shoving in new file extensions
        if ext in mime_types:
            self.set_header('Content-Type', mime_types[ext])

        await self.write_stream(result)
        self._send_hook('download_file', path=self.path)
Ejemplo n.º 11
0
    async def download(self, path, accept_url=False, revision=None, range=None, **kwargs):
        """Returns a ResponseWrapper (Stream) for the specified path
        raises FileNotFoundError if the status from S3 is not 200

        :param str path: Path to the key you want to download
        :param dict \*\*kwargs: Additional arguments that are ignored
        :rtype: :class:`waterbutler.core.streams.ResponseStreamReader`
        :raises: :class:`waterbutler.core.exceptions.DownloadError`
        """

        await self._check_region()

        if not path.is_file:
            raise exceptions.DownloadError('No file specified for download', code=400)

        if not revision or revision.lower() == 'latest':
            query_parameters = None
        else:
            query_parameters = {'versionId': revision}

        display_name = kwargs.get('display_name') or path.name
        response_headers = {
            'response-content-disposition': make_disposition(display_name)
        }

        url = functools.partial(
            self.bucket.new_key(path.path).generate_url,
            settings.TEMP_URL_SECS,
            query_parameters=query_parameters,
            response_headers=response_headers
        )

        if accept_url:
            return url()

        resp = await self.make_request(
            'GET',
            url,
            range=range,
            expects=(200, 206, ),
            throws=exceptions.DownloadError,
        )

        return streams.ResponseStreamReader(resp)
Ejemplo n.º 12
0
 def test_content_disposition(self, filename, expected):
     disposition = utils.make_disposition(filename)
     assert disposition == expected
Ejemplo n.º 13
0
 def test_content_disposition(self, filename, expected):
     disposition = utils.make_disposition(filename)
     assert disposition == expected