def _download_as_zip(self, req, parent, attachments=None): if attachments is None: attachments = self.viewable_attachments(web_context(req, parent)) total_size = sum(attachment.size for attachment in attachments) if total_size > self.max_zip_size: raise TracError( _("Maximum total attachment size: %(num)s", num=pretty_size(self.max_zip_size)), _("Download failed")) req.send_response(200) req.send_header('Content-Type', 'application/zip') filename = 'attachments-%s-%s.zip' % \ (parent.realm, re.sub(r'[/\\:]', '-', unicode(parent.id))) req.send_header('Content-Disposition', content_disposition('inline', filename)) buf = io.BytesIO() with ZipFile(buf, 'w', ZIP_DEFLATED) as zipfile: for attachment in attachments: zipinfo = create_zipinfo(attachment.filename, mtime=attachment.date, comment=attachment.description) try: with attachment.open() as fd: zipfile.writestr(zipinfo, fd.read()) except ResourceNotFound: pass # skip missing files zip_str = buf.getvalue() req.send_header("Content-Length", len(zip_str)) req.end_headers() req.write(zip_str) raise RequestDone()
def render_zip(req, filename, repos, root_node, iter_nodes): """Send a ZIP file containing the data corresponding to the `nodes` iterable. :type root_node: `~trac.versioncontrol.api.Node` :param root_node: optional ancestor for all the *nodes* :param iter_nodes: callable taking the optional *root_node* as input and generating the `~trac.versioncontrol.api.Node` for which the content should be added into the zip. """ req.send_response(200) req.send_header('Content-Type', 'application/zip') req.send_header('Content-Disposition', content_disposition('inline', filename)) if root_node: req.send_header('Last-Modified', http_date(root_node.last_modified)) root_path = root_node.path.rstrip('/') else: root_path = '' if root_path: root_path += '/' root_name = root_node.name + '/' else: root_name = '' root_len = len(root_path) buf = StringIO() zipfile = ZipFile(buf, 'w', ZIP_DEFLATED) for node in iter_nodes(root_node): if node is root_node: continue path = node.path.strip('/') assert path.startswith(root_path) path = root_name + path[root_len:] kwargs = {'mtime': node.last_modified} data = None if node.isfile: data = node.get_processed_content(eol_hint='CRLF').read() properties = node.get_properties() # Subversion specific if 'svn:special' in properties and data.startswith('link '): data = data[5:] kwargs['symlink'] = True if 'svn:executable' in properties: kwargs['executable'] = True elif node.isdir and path: kwargs['dir'] = True data = '' if data is not None: zipfile.writestr(create_zipinfo(path, **kwargs), data) zipfile.close() zip_str = buf.getvalue() req.send_header("Content-Length", len(zip_str)) req.end_headers() req.write(zip_str) raise RequestDone
def _download_as_zip(self, req, parent, attachments=None): if attachments is None: attachments = self.viewable_attachments(web_context(req, parent)) total_size = sum(attachment.size for attachment in attachments) if total_size > self.max_zip_size: raise TracError( _("Maximum total attachment size: %(num)s", num=pretty_size(self.max_zip_size)), _("Download failed")) req.send_response(200) req.send_header('Content-Type', 'application/zip') filename = 'attachments-%s-%s.zip' % \ (parent.realm, re.sub(r'[/\\:]', '-', unicode(parent.id))) req.send_header('Content-Disposition', content_disposition('inline', filename)) req.end_headers() def write_partial(fileobj, start): end = fileobj.tell() fileobj.seek(start, 0) remaining = end - start while remaining > 0: chunk = fileobj.read(min(remaining, 4096)) req.write(chunk) remaining -= len(chunk) fileobj.seek(end, 0) return end pos = 0 fileobj = TemporaryFile(prefix='trac-', suffix='.zip') try: zipfile = ZipFile(fileobj, 'w', ZIP_DEFLATED) for attachment in attachments: zipinfo = create_zipinfo(attachment.filename, mtime=attachment.date, comment=attachment.description) try: with attachment.open() as fd: zipfile.writestr(zipinfo, fd.read()) except ResourceNotFound: pass # skip missing files else: pos = write_partial(fileobj, pos) finally: try: zipfile.close() write_partial(fileobj, pos) finally: fileobj.close() raise RequestDone
def render_zip(req, filename, repos, root_node, iter_nodes): """Send a ZIP file containing the data corresponding to the `nodes` iterable. :type root_node: `~trac.versioncontrol.api.Node` :param root_node: optional ancestor for all the *nodes* :param iter_nodes: callable taking the optional *root_node* as input and generating the `~trac.versioncontrol.api.Node` for which the content should be added into the zip. """ req.send_response(200) req.send_header('Content-Type', 'application/zip') req.send_header('Content-Disposition', content_disposition('inline', filename)) if root_node: req.send_header('Last-Modified', http_date(root_node.last_modified)) root_path = root_node.path.rstrip('/') else: root_path = '' if root_path: root_path += '/' root_name = root_node.name + '/' else: root_name = '' root_len = len(root_path) req.end_headers() def write_partial(fileobj, start): end = fileobj.tell() fileobj.seek(start, 0) remaining = end - start while remaining > 0: chunk = fileobj.read(min(remaining, 4096)) req.write(chunk) remaining -= len(chunk) fileobj.seek(end, 0) return end pos = 0 with TemporaryFile(prefix='trac-', suffix='.zip') as fileobj: with ZipFile(fileobj, 'w', ZIP_DEFLATED) as zipfile: for node in iter_nodes(root_node): if node is root_node: continue path = node.path.strip('/') assert path.startswith(root_path) path = root_name + path[root_len:] kwargs = {'mtime': node.last_modified} data = None if node.isfile: with content_closing( node.get_processed_content(eol_hint='CRLF')) \ as content: data = content.read() props = node.get_properties() # Subversion specific if 'svn:special' in props and data.startswith('link '): data = data[5:] kwargs['symlink'] = True if 'svn:executable' in props: kwargs['executable'] = True elif node.isdir and path: kwargs['dir'] = True data = '' if data is not None: zipfile.writestr(create_zipinfo(path, **kwargs), data) pos = write_partial(fileobj, pos) write_partial(fileobj, pos) raise RequestDone