Source code for invenio_previewer.extensions.zip
# -*- coding: utf-8 -*-
#
# This file is part of Invenio.
# Copyright (C) 2013, 2014, 2015, 2016 CERN.
#
# Invenio is free software; you can redistribute it
# and/or modify it under the terms of the GNU General Public License as
# published by the Free Software Foundation; either version 2 of the
# License, or (at your option) any later version.
#
# Invenio is distributed in the hope that it will be
# useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Invenio; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston,
# MA 02111-1307, USA.
#
# In applying this license, CERN does not
# waive the privileges and immunities granted to it by virtue of its status
# as an Intergovernmental Organization or submit itself to any jurisdiction.
"""Simple ZIP archive previewer."""
from __future__ import absolute_import, print_function
import os
import zipfile
import cchardet as chardet
from flask import current_app, render_template
from six import binary_type
from .._compat import text_type
from ..proxies import current_previewer
previewable_extensions = ['zip']
[docs]def make_tree(file):
"""Create tree structure from ZIP archive."""
max_files_count = current_app.config.get('PREVIEWER_ZIP_MAX_FILES', 1000)
tree = {'type': 'folder', 'id': -1, 'children': {}}
try:
with file.open() as fp:
zf = zipfile.ZipFile(fp)
# Detect filenames encoding.
sample = ' '.join(zf.namelist()[:max_files_count])
if not isinstance(sample, binary_type):
sample = sample.encode('utf-16be')
encoding = chardet.detect(sample).get('encoding', 'utf-8')
for i, info in enumerate(zf.infolist()):
if i > max_files_count:
raise BufferError('Too many files inside the ZIP file.')
comps = info.filename.split(os.sep)
node = tree
for c in comps:
if not isinstance(c, text_type):
c = c.decode(encoding)
if c not in node['children']:
if c == '':
node['type'] = 'folder'
continue
node['children'][c] = {
'name': c,
'type': 'item',
'id': 'item{0}'.format(i),
'children': {}
}
node = node['children'][c]
node['size'] = info.file_size
except BufferError:
return tree, True, None
except (zipfile.LargeZipFile):
return tree, False, 'Zipfile is too large to be previewed.'
except Exception as e:
current_app.logger.warning(str(e), exc_info=True)
return tree, False, 'Zipfile is not previewable.'
return tree, False, None
[docs]def children_to_list(node):
"""Organize children structure."""
if node['type'] == 'item' and len(node['children']) == 0:
del node['children']
else:
node['type'] = 'folder'
node['children'] = list(node['children'].values())
node['children'].sort(key=lambda x: x['name'])
node['children'] = map(children_to_list, node['children'])
return node
[docs]def can_preview(file):
"""Return True if filetype can be previewed."""
return file.is_local() and file.has_extensions('.zip')
[docs]def preview(file):
"""Return appropriate template and pass the file and an embed flag."""
tree, limit_reached, error = make_tree(file)
list = children_to_list(tree)['children']
return render_template(
"invenio_previewer/zip.html",
file=file,
tree=list,
limit_reached=limit_reached,
error=error,
js_bundles=current_previewer.js_bundles + ['previewer_fullscreen_js'],
css_bundles=current_previewer.css_bundles,
)