mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Fix #6928 (Calibre corrupts EPUB)
This commit is contained in:
parent
ee0b2541c8
commit
54c3e009d5
@ -15,7 +15,7 @@ from calibre.customize.ui import available_input_formats
|
||||
from calibre.ebooks.metadata.opf2 import OPF
|
||||
from calibre.ptempfile import TemporaryDirectory
|
||||
from calibre.ebooks.chardet import xml_to_unicode
|
||||
from calibre.utils.zipfile import safe_replace, ZipFile
|
||||
from calibre.utils.zipfile import safe_replace
|
||||
from calibre.utils.config import DynamicConfig
|
||||
from calibre.utils.logging import Log
|
||||
from calibre import guess_type, prints
|
||||
@ -294,12 +294,8 @@ class EbookIterator(object):
|
||||
zf = open(self.pathtoebook, 'r+b')
|
||||
except IOError:
|
||||
return
|
||||
zipf = ZipFile(zf, mode='a')
|
||||
for name in zipf.namelist():
|
||||
if name == 'META-INF/calibre_bookmarks.txt':
|
||||
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat))
|
||||
return
|
||||
zipf.writestr('META-INF/calibre_bookmarks.txt', dat)
|
||||
safe_replace(zf, 'META-INF/calibre_bookmarks.txt', StringIO(dat),
|
||||
add_missing=True)
|
||||
else:
|
||||
self.config['bookmarks_'+self.pathtoebook] = dat
|
||||
|
||||
|
@ -281,7 +281,7 @@ class ZipInfo (object):
|
||||
'file_offset',
|
||||
)
|
||||
|
||||
def __init__(self, filename="NoName", date_time=(1980,1,1,0,0,0)):
|
||||
def __init__(self, filename=u"NoName", date_time=(1980,1,1,0,0,0)):
|
||||
self.orig_filename = filename # Original file name in archive
|
||||
|
||||
# Terminate the file name at the first null byte. Null bytes in file
|
||||
@ -1362,30 +1362,42 @@ class ZipFile:
|
||||
self.fp.close()
|
||||
self.fp = None
|
||||
|
||||
def safe_replace(zipstream, name, datastream, extra_replacements={}):
|
||||
def safe_replace(zipstream, name, datastream, extra_replacements={},
|
||||
add_missing=False):
|
||||
'''
|
||||
Replace a file in a zip file in a safe manner. This proceeds by extracting
|
||||
and re-creating the zipfile. This is necessary because :method:`ZipFile.replace`
|
||||
sometimes created corrupted zip files.
|
||||
|
||||
|
||||
:param zipstream: Stream from a zip file
|
||||
:param name: The name of the file to replace
|
||||
:param datastream: The data to replace the file with.
|
||||
:param extra_replacements: Extra replacements. Mapping of name to file-like
|
||||
objects
|
||||
:param add_missing: If a replacement does not exist in the zip file, it is
|
||||
added. Use with care as currently parent directories
|
||||
are not created.
|
||||
|
||||
'''
|
||||
z = ZipFile(zipstream, 'r')
|
||||
replacements = {name:datastream}
|
||||
replacements.update(extra_replacements)
|
||||
names = frozenset(replacements.keys())
|
||||
found = set([])
|
||||
with SpooledTemporaryFile(max_size=100*1024*1024) as temp:
|
||||
ztemp = ZipFile(temp, 'w')
|
||||
for obj in z.infolist():
|
||||
if isinstance(obj.filename, unicode):
|
||||
obj.flag_bits |= 0x16 # Set isUTF-8 bit
|
||||
if obj.filename in names:
|
||||
ztemp.writestr(obj, replacements[obj.filename].read())
|
||||
found.add(obj.filename)
|
||||
else:
|
||||
ztemp.writestr(obj, z.read_raw(obj), raw_bytes=True)
|
||||
if add_missing:
|
||||
for name in names - found:
|
||||
ztemp.writestr(name, replacements[name].read())
|
||||
ztemp.close()
|
||||
z.close()
|
||||
temp.seek(0)
|
||||
|
Loading…
x
Reference in New Issue
Block a user