def downloadMultiple(self, params): include = params.get('include', 'all') if include not in {'all', 'images', 'metadata'}: raise ValidationException( 'Param "include" must be one of: "all", "images", "metadata"', 'include') if 'filter' in params and 'imageIds' in params: raise RestException('Only one of "filter" or "imageIds" may be specified.') if 'filter' in params: query = self._parseFilter(params['filter']) elif 'imageIds' in params: query = self._parseImageIds(params['imageIds']) else: query = {} user = self.getCurrentUser() downloadFileName = 'ISIC-images' images = Image().filterResultsByPermission( Image().find(query, sort=[('name', 1)]), user=user, level=AccessType.READ, limit=0, offset=0) # Prevent cursor timeouts by eagerly evaluating the whole query images = list(images) imagesZipGenerator = self._imagesZipGenerator(downloadFileName, images, include) setResponseHeader('Content-Type', 'application/zip') setResponseHeader('Content-Disposition', 'attachment; filename="%s.zip"' % downloadFileName) return lambda: imagesZipGenerator
def getTilesRegion(self, item, params): params = self._parseParams(params, True, [ ('left', float, 'region', 'left'), ('top', float, 'region', 'top'), ('right', float, 'region', 'right'), ('bottom', float, 'region', 'bottom'), ('regionWidth', float, 'region', 'width'), ('regionHeight', float, 'region', 'height'), ('units', str, 'region', 'units'), ('width', int, 'output', 'maxWidth'), ('height', int, 'output', 'maxHeight'), ('magnification', float, 'scale', 'magnification'), ('mm_x', float, 'scale', 'mm_x'), ('mm_y', float, 'scale', 'mm_y'), ('exact', bool, 'scale', 'exact'), ('encoding', str), ('jpegQuality', int), ('jpegSubsampling', int), ]) try: regionData, regionMime = self.imageItemModel.getRegion( item, **params) except TileGeneralException as e: raise RestException(e.message) except ValueError as e: raise RestException('Value Error: %s' % e.message) setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def setContentHeaders(self, file, offset, endByte, contentDisposition=None): """ Sets the Content-Length, Content-Disposition, Content-Type, and also the Content-Range header if this is a partial download. :param file: The file being downloaded. :param offset: The start byte of the download. :type offset: int :param endByte: The end byte of the download (non-inclusive). :type endByte: int :param contentDisposition: Content-Disposition response header disposition-type value, if None, Content-Disposition will be set to 'attachment; filename=$filename'. :type contentDisposition: str or None """ setResponseHeader( 'Content-Type', file.get('mimeType') or 'application/octet-stream') if contentDisposition == 'inline': setResponseHeader( 'Content-Disposition', 'inline; filename="%s"' % file['name']) else: setResponseHeader( 'Content-Disposition', 'attachment; filename="%s"' % file['name']) setResponseHeader('Content-Length', max(endByte - offset, 0)) if (offset or endByte < file['size']) and file['size']: setResponseHeader( 'Content-Range', 'bytes %d-%d/%d' % (offset, endByte - 1, file['size']))
def _getTile(self, item, z, x, y, imageArgs): """ Get an large image tile. :param item: the item to get a tile from. :param z: tile layer number (0 is the most zoomed-out). .param x: the X coordinate of the tile (0 is the left side). .param y: the Y coordinate of the tile (0 is the top). :param imageArgs: additional arguments to use when fetching image data. :return: a function that returns the raw image data. """ try: x, y, z = int(x), int(y), int(z) except ValueError: raise RestException('x, y, and z must be integers', code=400) if x < 0 or y < 0 or z < 0: raise RestException('x, y, and z must be positive integers', code=400) try: tileData, tileMime = self.imageItemModel.getTile( item, x, y, z, **imageArgs) except TileGeneralException as e: raise RestException(e.message, code=404) setResponseHeader('Content-Type', tileMime) setRawResponse() return tileData
def getTile(self, itemId, z, x, y, params): item = loadmodelcache.loadModel( self, 'item', id=itemId, allowCookie=True, level=AccessType.READ) # Explicitly set a expires time to encourage browsers to cache this for # a while. setResponseHeader('Expires', cherrypy.lib.httputil.HTTPDate( cherrypy.serving.response.time + 600)) return self._getTile(item, z, x, y, params)
def thumbnail(self, image, params): width = int(params.get('width', 256)) height = int(params.get('height', 256)) thumbData, thumbMime = ImageItem().getThumbnail(image, width=width, height=height) # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', thumbMime) return thumbData
def getStudy(self, study, params): if params.get('format') == 'csv': setResponseHeader('Content-Type', 'text/csv') setResponseHeader('Content-Disposition', 'attachment; filename="%s.csv"' % study['name']) return functools.partial(self._getStudyCSVStream, study) else: user = self.getCurrentUser() return Study().filter(study, user)
def testAnalysisXmlDetection(self, params): """Return the nuclei detection XML spec as a test case.""" xml_file = os.path.abspath( os.path.join(os.path.dirname(__file__), 'test_files', 'test_analysis_detection.xml')) with open(xml_file) as f: xml = f.read() setResponseHeader('Content-Type', 'application/xml') setRawResponse() return xml
def testAnalysisXmlFeatures(self, params): """Return the nuclei feature classification XML spec as a test case.""" xml_file = os.path.abspath( os.path.join(os.path.dirname(__file__), '..', 'histomicstk', 'cli', 'ComputeNucleiFeatures', 'ComputeNucleiFeatures.xml')) with open(xml_file) as f: xml = f.read() setResponseHeader('Content-Type', 'application/xml') setRawResponse() return xml
def downloadFile(self, file, offset=0, headers=True, endByte=None, contentDisposition=None, extraParameters=None, **kwargs): """ Returns a generator function that will be used to stream the file from the database to the response. """ if endByte is None or endByte > file['size']: endByte = file['size'] if headers: setResponseHeader('Accept-Ranges', 'bytes') self.setContentHeaders(file, offset, endByte, contentDisposition) # If the file is empty, we stop here if endByte - offset <= 0: return lambda: '' n = 0 chunkOffset = 0 # We must "seek" to the correct chunk index and local offset if offset > 0: n = offset // file['chunkSize'] chunkOffset = offset % file['chunkSize'] cursor = self.chunkColl.find({ 'uuid': file['chunkUuid'], 'n': {'$gte': n} }, projection=['data']).sort('n', pymongo.ASCENDING) def stream(): co = chunkOffset # Can't assign to outer scope without "nonlocal" position = offset shouldBreak = False for chunk in cursor: chunkLen = len(chunk['data']) if position + chunkLen - co > endByte: chunkLen = endByte - position + co shouldBreak = True yield chunk['data'][co:chunkLen] if shouldBreak: break position += chunkLen - co if co > 0: co = 0 return stream
def getTile(self, itemId, z, x, y, params): _adjustParams(params) item = loadmodelcache.loadModel( self, 'item', id=itemId, allowCookie=True, level=AccessType.READ) # Explicitly set a expires time to encourage browsers to cache this for # a while. setResponseHeader('Expires', cherrypy.lib.httputil.HTTPDate( cherrypy.serving.response.time + 600)) redirect = params.get('redirect', False) if redirect not in ('any', 'exact', 'encoding'): redirect = False return self._getTile(item, z, x, y, params, mayRedirect=redirect)
def exportTale(self, tale, taleFormat): user = self.getCurrentUser() zip_name = str(tale['_id']) if taleFormat == 'bagit': exporter = BagTaleExporter(tale, user, expand_folders=True) elif taleFormat == 'native': exporter = NativeTaleExporter(tale, user) setResponseHeader('Content-Type', 'application/zip') setContentDisposition(zip_name + '.zip') return exporter.stream
def download(self, colormap, name): setContentDisposition(name) def stream(): yield json.dumps({ 'colormap': colormap['colormap'] }, sort_keys=True, allow_nan=False, cls=JsonEncoder).encode('utf8') setResponseHeader('Content-Type', 'application/json') return stream
def _forward(self): """ Forward a request to the same path with a slash added. """ result = '\n'.join([ '<html>', '<head><title>301 Moved Permanently</title></head>', '<body>', '<center><h1>301 Moved Permanently</h1></center>', '</body>', '</html>']) setResponseHeader('Location', cherrypy.request.path_info + '/') setResponseHeader('Content-Type', 'text/html') setRawResponse() cherrypy.response.status = 301 return result.encode('utf8')
def getTile(self, itemId, z, x, y, params): item = loadmodelcache.loadModel(self, 'item', id=itemId, allowCookie=True, level=AccessType.READ) # Explicitly set a expires time to encourage browsers to cache this for # a while. setResponseHeader( 'Expires', cherrypy.lib.httputil.HTTPDate(cherrypy.serving.response.time + 600)) return self._getTile(item, z, x, y, params)
def getTile(self, image, z, x, y, params): try: x, y, z = int(x), int(y), int(z) except ValueError: raise RestException('x, y, and z must be integers') if x < 0 or y < 0 or z < 0: raise RestException('x, y, and z must be positive integers') try: tileData, tileMime = ImageItem().getTile(image, x, y, z) except TileGeneralException as e: raise RestException(e.message, code=404) setResponseHeader('Content-Type', tileMime) setRawResponse() return tileData
def downloadKeyFile(self, file, algo): self._validateAlgo(algo) if algo not in file: raise RestException('This file does not have the %s hash computed.' % algo) keyFileBody = '%s\n' % file[algo] name = '.'.join((file['name'], algo)) setResponseHeader('Content-Length', len(keyFileBody)) setResponseHeader('Content-Type', 'text/plain') setContentDisposition(name) setRawResponse() return keyFileBody
def thumbnail(self, segmentation, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException(f'Unallowed contentDisposition type "{contentDisp}".', 'contentDisposition') # TODO: convert this to make Segmentation use an AccessControlMixin image = Image().load( segmentation['imageId'], level=AccessType.READ, user=self.getCurrentUser(), exc=True) width = int(params.get('width', 256)) thumbnailImageStream = Segmentation().boundaryThumbnail(segmentation, image, width) if thumbnailImageStream is None: raise RestException('This segmentation is failed, and thus has no thumbnail.', code=410) thumbnailImageData = thumbnailImageStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = f'{image["name"]}_segmentation_thumbnail.jpg' if contentDisp == 'inline': setResponseHeader('Content-Disposition', f'inline; filename="{contentName}"') else: setResponseHeader('Content-Disposition', f'attachment; filename="{contentName}"') setResponseHeader('Content-Length', len(thumbnailImageData)) return thumbnailImageData
def thumbnail(self, segmentation, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') # TODO: convert this to make Segmentation use an AccessControlMixin image = Image().load( segmentation['imageId'], level=AccessType.READ, user=self.getCurrentUser(), exc=True) width = int(params.get('width', 256)) thumbnailImageStream = Segmentation().boundaryThumbnail(segmentation, image, width) if thumbnailImageStream is None: raise RestException('This segmentation is failed, and thus has no thumbnail.', code=410) thumbnailImageData = thumbnailImageStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_segmentation_thumbnail.jpg' % image['name'] if contentDisp == 'inline': setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(thumbnailImageData)) return thumbnailImageData
def getAnnotationMarkupRendered(self, annotation, featureId, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') self._ensureMarkup(annotation, featureId) renderData = Annotation().renderMarkup(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage(renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = 'annotation_%s_%s.jpg' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',') ) if contentDisp == 'inline': setResponseHeader( 'Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader( 'Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(renderEncodedData)) return renderEncodedData
def _getHdf5Dataset(self, item): try: setResponseHeader('Content-Type', 'image/png') setRawResponse() hdf5Path = [i for i in item['meta'] if 'hdf5Path' in i.keys()][0]['hdf5Path'] pathInHdf5 = [i for i in item['meta'] if 'pathInHdf5' in i.keys()][0]['pathInHdf5'] figure = render_hdf5_dataset(hdf5Path, pathInHdf5) buf = BytesIO() figure.savefig(buf, format='png') return b64encode(buf.getvalue()) except: pass
def getAnnotationMarkupRendered(self, annotation, featureId, params): contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in { 'inline', 'attachment' }: raise ValidationException( f'Unallowed contentDisposition type "{contentDisp}".', 'contentDisposition') self._ensureMarkup(annotation, featureId) renderData = Annotation().renderMarkup(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage( renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = 'annotation_%s_%s.jpg' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',')) if contentDisp == 'inline': setResponseHeader('Content-Disposition', f'inline; filename="{contentName}"') else: setResponseHeader('Content-Disposition', f'attachment; filename="{contentName}"') setResponseHeader('Content-Length', str(len(renderEncodedData))) return renderEncodedData
def getDZITile(self, item, level, xandy, params): _adjustParams(params) tilesize = int(params.get('tilesize', 256)) if tilesize & (tilesize - 1): raise RestException('Invalid tilesize', code=400) overlap = int(params.get('overlap', 0)) if overlap < 0: raise RestException('Invalid overlap', code=400) x, y = [int(xy) for xy in xandy.split('.')[0].split('_')] _handleETag('getDZITile', item, level, xandy, params) metadata = self.imageItemModel.getMetadata(item, **params) level = int(level) maxlevel = int(math.ceil(math.log(max( metadata['sizeX'], metadata['sizeY'])) / math.log(2))) if level < 1 or level > maxlevel: raise RestException('level must be between 1 and the image scale', code=400) lfactor = 2 ** (maxlevel - level) region = { 'left': (x * tilesize - overlap) * lfactor, 'top': (y * tilesize - overlap) * lfactor, 'right': ((x + 1) * tilesize + overlap) * lfactor, 'bottom': ((y + 1) * tilesize + overlap) * lfactor, } width = height = tilesize + overlap * 2 if region['left'] < 0: width += int(region['left'] / lfactor) region['left'] = 0 if region['top'] < 0: height += int(region['top'] / lfactor) region['top'] = 0 if region['left'] >= metadata['sizeX']: raise RestException('x is outside layer', code=400) if region['top'] >= metadata['sizeY']: raise RestException('y is outside layer', code=400) if region['left'] < metadata['sizeX'] and region['right'] > metadata['sizeX']: region['right'] = metadata['sizeX'] width = int(math.ceil(float(region['right'] - region['left']) / lfactor)) if region['top'] < metadata['sizeY'] and region['bottom'] > metadata['sizeY']: region['bottom'] = metadata['sizeY'] height = int(math.ceil(float(region['bottom'] - region['top']) / lfactor)) regionData, regionMime = self.imageItemModel.getRegion( item, region=region, output=dict(maxWidth=width, maxHeight=height), **params) setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def export(self, folder, params): setResponseHeader('Content-Type', 'application/zip') setContentDisposition(folder['name'] + '.zip') def stream(): zip = ziputil.ZipGenerator(folder['name']) for data in zip.addFile(DetectionResource.generateKPFContent(folder), folder['name'] + '.geom.kpf'): yield data for data in zip.addFile(TypesResource.generateKPFContent(folder), folder['name'] + '.types.kpf'): yield data for data in zip.addFile(ActivitiesResource.generateKPFContent(folder), folder['name'] + '.activities.kpf'): yield data yield zip.footer() return stream
def scan_positions(self, id, user, type): path = self._get_path_to_type(type) if type == 'electron': return self._get_h5_dataset(id, user, path) elif type == 'raw': setResponseHeader('Content-Type', 'application/octet-stream') def _stream(): with self._open_h5py_file(id, user) as rf: dataset = rf[path] array = np.array([i for i in range(dataset.shape[0])]) yield array.tobytes() return _stream raise RestException('In scan_positions, unknown type: ' + type)
def export_all(self, folder, excludeBelowThreshold): _, gen = self._generate_detections(folder, excludeBelowThreshold) setResponseHeader('Content-Type', 'application/zip') setContentDisposition(folder['name'] + '.zip') user = self.getCurrentUser() def stream(): z = ziputil.ZipGenerator(folder['name']) for (path, file) in Folder().fileList(folder, user=user, subpath=False): for data in z.addFile(file, path): yield data for data in z.addFile(gen, "output_tracks.csv"): yield data yield z.footer() return stream
def file_download(self, event, path, root, user=None): fobj = self.vFile(path, root) rangeHeader = cherrypy.lib.httputil.get_ranges( cherrypy.request.headers.get("Range"), fobj.get("size", 0) ) # The HTTP Range header takes precedence over query params if rangeHeader and len(rangeHeader): # Currently we only support a single range. offset, endByte = rangeHeader[0] else: endByte = min( int(event.info["params"].get("endByte", fobj["size"])), fobj["size"] ) offset = int(event.info["params"].get("offset", "0")) setResponseHeader("Accept-Ranges", "bytes") setResponseHeader("Content-Type", "application/octet-stream") setResponseHeader("Content-Length", max(endByte - offset, 0)) if (offset or endByte < fobj["size"]) and fobj["size"]: setResponseHeader( "Content-Range", "bytes %d-%d/%d" % (offset, endByte - 1, fobj["size"]) ) disp = event.info["params"].get("contentDisposition", "attachment") setResponseHeader( "Content-Disposition", '{}; filename="{}"'.format(disp, fobj["name"]) ) def stream(): bytesRead = offset with path.open(mode="rb") as f: if offset > 0: f.seek(offset) while True: readLen = min(BUF_SIZE, endByte - bytesRead) if readLen <= 0: break data = f.read(readLen) bytesRead += readLen # if not data: # break yield data event.preventDefault().addResponse(stream)
def downloadFile(self, file, offset=0, headers=True, endByte=None, contentDisposition=None, extraParameters=None, **kwargs): """ Returns a generator function that will be used to stream the file from disk to the response. """ if endByte is None or endByte > file['size']: endByte = file['size'] path = self.fullPath(file) if not os.path.isfile(path): raise GirderException( 'File %s does not exist.' % path, 'girder.utility.filesystem_assetstore_adapter.' 'file-does-not-exist') if headers: setResponseHeader('Accept-Ranges', 'bytes') self.setContentHeaders(file, offset, endByte, contentDisposition) def stream(): bytesRead = offset with open(path, 'rb') as f: if offset > 0: f.seek(offset) while True: readLen = min(BUF_SIZE, endByte - bytesRead) if readLen <= 0: break data = f.read(readLen) bytesRead += readLen if not data: break yield data return stream
def downloadFile(self, file, offset=0, headers=True, endByte=None, contentDisposition=None, extraParameters=None, **kwargs): if endByte is None or endByte > file['size']: endByte = file['size'] if headers: setResponseHeader('Accept-Ranges', 'bytes') self.setContentHeaders(file, offset, endByte, contentDisposition) if file['hdfs'].get('imported'): path = file['hdfs']['path'] else: path = self._absPath(file) def stream(): position = 0 fileStream = self.client.cat([path]).next() shouldBreak = False for chunk in fileStream: chunkLen = len(chunk) if position < offset: if position + chunkLen > offset: if position + chunkLen > endByte: chunkLen = endByte - position shouldBreak = True yield chunk[offset - position:chunkLen] else: if position + chunkLen > endByte: chunkLen = endByte - position shouldBreak = True yield chunk[:chunkLen] position += chunkLen if shouldBreak: break return stream
def importAnnotation(self, folder, params): # TODO: Check if is a clip folder detectionItem, activitiesItem, typesItem = self._getAnnotationItems(folder) setResponseHeader('Content-Length', 1000000) def gen(): self._importActivities(folder['_id'], activitiesItem) self._importTypes(folder['_id'], typesItem) lastProgress = 0 import math for percentage in self._importDetection(folder['_id'], detectionItem): progress = int(math.floor(percentage * 100)) if lastProgress != progress: yield '1' * (progress - lastProgress) * 10000 lastProgress = progress yield '1' * (100 - lastProgress) * 10000 return gen
def getTile(self, itemId, z, x, y, params): _adjustParams(params) item = loadmodelcache.loadModel( self, 'item', id=itemId, allowCookie=True, level=AccessType.READ) # Explicitly set a expires time to encourage browsers to cache this for # a while. setResponseHeader('Expires', cherrypy.lib.httputil.HTTPDate( cherrypy.serving.response.time + 600)) redirect = params.get('redirect', False) if redirect not in ('any', 'exact', 'encoding'): redirect = False typeList = [ ('normalize', bool), ('normalizeMin', float), ('normalizeMax', float), ('label', bool), ('invertLabel', bool), ('flattenLabel', bool), ('oneHot', bool), ('bit', int), ] params = self._parseParams(params, True, typeList) if 'exclude' in params: # TODO: error handling params['exclude'] = [int(s) for s in params['exclude'].split(',')] if Colormap and 'colormapId' in params: # colormap = Colormap().load(params['colormapId'], # force=True, exc=True) # user=self.getCurrentUser(), # level=AccessType.READ) colormap = Colormap().load(params['colormapId'], force=True, exc=True) del params['colormapId'] if 'bit' in params: params['colormap'] = colormap['colormap'] else: # TODO: abstract in colormap try: params['colormap'] = bytearray(colormap['binary']) except (KeyError, TypeError): raise RestException('Invalid colormap on server', code=500) return self._getTile(item, z, x, y, params, mayRedirect=redirect)
def _handleETag(key, item, *args, **kwargs): """ Add or check an ETag header. :param key: key for making a distinc etag. :param item: item used for the item _id and updated timestamp. :param *args, **kwargs: additional arguments for generating an etag. """ etag = hashlib.md5(strhash(key, str(item['_id']), *args, **kwargs).encode()).hexdigest() setResponseHeader('ETag', etag) conditions = [str(x) for x in cherrypy.request.headers.elements('If-Match') or []] if conditions and not (conditions == ['*'] or etag in conditions): raise cherrypy.HTTPError( 412, 'If-Match failed: ETag %r did not match %r' % (etag, conditions)) conditions = [str(x) for x in cherrypy.request.headers.elements('If-None-Match') or []] if conditions == ['*'] or etag in conditions: raise cherrypy.HTTPRedirect([], 304) # Explicitly set a max-ago to recheck the cahe after a while setResponseHeader('Cache-control', 'max-age=600')
def all_frames(self, id, user, type, limit=None, offset=None): path = self._get_path_to_type(type) # Ensure limit and offset are reasonable with self._open_h5py_file(id, user) as rf: limit, offset = self._check_limit_and_offset( rf[path], limit, offset) setResponseHeader('Content-Type', 'application/octet-stream') def _stream(): nonlocal id nonlocal user with self._open_h5py_file(id, user) as rf: dataset = rf[path] for data in self._get_vlen_dataset_in_chunks( dataset, limit, offset): yield msgpack.packb(data, use_bin_type=True) return _stream
def getTilesThumbnail(self, item, params): params = self._parseParams(params, True, [ ('width', int), ('height', int), ('jpegQuality', int), ('jpegSubsampling', int), ('encoding', str), ]) try: result = self.imageItemModel.getThumbnail(item, **params) except TileGeneralException as e: raise RestException(e.message) except ValueError as e: raise RestException('Value Error: %s' % e.message) if not isinstance(result, tuple): return result thumbData, thumbMime = result setResponseHeader('Content-Type', thumbMime) setRawResponse() return thumbData
def getTilesRegion(self, item, params): _adjustParams(params) params = self._parseParams(params, True, [ ('left', float, 'region', 'left'), ('top', float, 'region', 'top'), ('right', float, 'region', 'right'), ('bottom', float, 'region', 'bottom'), ('regionWidth', float, 'region', 'width'), ('regionHeight', float, 'region', 'height'), ('units', str, 'region', 'units'), ('unitsWH', str, 'region', 'unitsWH'), ('width', int, 'output', 'maxWidth'), ('height', int, 'output', 'maxHeight'), ('fill', str), ('magnification', float, 'scale', 'magnification'), ('mm_x', float, 'scale', 'mm_x'), ('mm_y', float, 'scale', 'mm_y'), ('exact', bool, 'scale', 'exact'), ('frame', int), ('encoding', str), ('jpegQuality', int), ('jpegSubsampling', int), ('tiffCompression', str), ('style', str), ('resample', 'boolOrInt'), ('contentDisposition', str), ]) _handleETag('getTilesRegion', item, params) try: regionData, regionMime = self.imageItemModel.getRegion( item, **params) except TileGeneralException as e: raise RestException(e.args[0]) except ValueError as e: raise RestException('Value Error: %s' % e.args[0]) self._setContentDisposition( item, params.get('contentDisposition'), regionMime, 'region') setResponseHeader('Content-Type', regionMime) setRawResponse() return regionData
def getAnnotationMarkupMask(self, annotation, featureId, params): self._ensureMarkup(annotation, featureId) markupFile = Annotation().getMarkupFile(annotation, featureId) if markupFile: return File().download(markupFile, headers=True, contentDisposition='inline') else: image = Image().load(annotation['imageId'], force=True, exc=True) markupMask = numpy.zeros( ( image['meta']['acquisition']['pixelsY'], image['meta']['acquisition']['pixelsX'] ), dtype=numpy.uint8 ) markupMaskEncodedStream = ScikitSegmentationHelper.writeImage(markupMask, 'png') markupMaskEncodedData = markupMaskEncodedStream.getvalue() setRawResponse() setResponseHeader('Content-Type', 'image/png') contentName = 'annotation_%s_%s.png' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',') ) setResponseHeader('Content-Disposition', 'inline; filename="%s"' % contentName) setResponseHeader('Content-Length', len(markupMaskEncodedData)) return markupMaskEncodedData
def setContentHeaders(self, file, offset, endByte, contentDisposition=None): """ Sets the Content-Length, Content-Disposition, Content-Type, and also the Content-Range header if this is a partial download. :param file: The file being downloaded. :param offset: The start byte of the download. :type offset: int :param endByte: The end byte of the download (non-inclusive). :type endByte: int :param contentDisposition: Content-Disposition response header disposition-type value, if None, Content-Disposition will be set to 'attachment; filename=$filename'. :type contentDisposition: str or None """ isRangeRequest = cherrypy.request.headers.get('Range') setResponseHeader('Content-Type', file.get('mimeType') or 'application/octet-stream') setContentDisposition(file['name'], contentDisposition or 'attachment') setResponseHeader('Content-Length', max(endByte - offset, 0)) if (offset or endByte < file['size'] or isRangeRequest) and file['size']: setResponseHeader( 'Content-Range', 'bytes %d-%d/%d' % (offset, endByte - 1, file['size']))
def getAnnotationMarkupMask(self, annotation, featureId, params): self._ensureMarkup(annotation, featureId) markupFile = Annotation().getMarkupFile(annotation, featureId) if markupFile: return File().download(markupFile, headers=True, contentDisposition='inline') else: image = Image().load(annotation['imageId'], force=True, exc=True) markupMask = numpy.zeros((image['meta']['acquisition']['pixelsY'], image['meta']['acquisition']['pixelsX']), dtype=numpy.uint8) markupMaskEncodedStream = ScikitSegmentationHelper.writeImage( markupMask, 'png') markupMaskEncodedData = markupMaskEncodedStream.getvalue() setRawResponse() setResponseHeader('Content-Type', 'image/png') contentName = 'annotation_%s_%s.png' % ( annotation['_id'], # Rename features to ensure the file is downloadable on Windows featureId.replace(' : ', ' ; ').replace('/', ',')) setResponseHeader('Content-Disposition', f'inline; filename="{contentName}"') setResponseHeader('Content-Length', str(len(markupMaskEncodedData))) return markupMaskEncodedData
def renderAnnotation(self, annotation, params): Study = self.model('study', 'isic_archive') Annotation = self.model('annotation', 'isic_archive') contentDisp = params.get('contentDisposition', None) if contentDisp is not None and contentDisp not in {'inline', 'attachment'}: raise ValidationException('Unallowed contentDisposition type "%s".' % contentDisp, 'contentDisposition') self.requireParams(['featureId'], params) featureId = params['featureId'] study = Study.load(annotation['meta']['studyId'], force=True, exc=True) featureset = Study.getFeatureset(study) if not any(featureId == feature['id'] for feature in featureset['localFeatures']): raise ValidationException('Invalid featureId.', 'featureId') if Annotation.getState(annotation) != Study.State.COMPLETE: raise RestException('Only complete annotations can be rendered.') renderData = Annotation.renderAnnotation(annotation, featureId) renderEncodedStream = ScikitSegmentationHelper.writeImage(renderData, 'jpeg') renderEncodedData = renderEncodedStream.getvalue() # Only setRawResponse now, as this handler may return a JSON error # earlier setRawResponse() setResponseHeader('Content-Type', 'image/jpeg') contentName = '%s_%s_annotation.jpg' % ( annotation['_id'], featureId.replace('/', ',') # TODO: replace with a better character ) if contentDisp == 'inline': setResponseHeader( 'Content-Disposition', 'inline; filename="%s"' % contentName) else: setResponseHeader( 'Content-Disposition', 'attachment; filename="%s"' % contentName) setResponseHeader('Content-Length', len(renderEncodedData)) return renderEncodedData
def downloadKeyFile(self, file, algo, params): self._validateAlgo(algo) if algo not in file: raise RestException('This file does not have the %s hash computed.' % algo) hash = file[algo] name = '.'.join((file['name'], algo)) setResponseHeader('Content-Length', len(hash)) setResponseHeader('Content-Type', 'text/plain') setResponseHeader('Content-Disposition', 'attachment; filename="%s"' % name) setRawResponse() return hash
def rawReturningText(self, params): setResponseHeader('Content-Type', 'text/plain') return u'this is not encoded \U0001F44D'