ОБНОВЛЕНИЕ: я могу воспроизвести это, установив размер PNG, превышающий произвольное значение (например, 700 x 700 pt не выполняется). Под произвольным значением пишет и читает нормально. Я не уверен, где именно эта линия.
Я использую zip-архив в качестве формата файла документа. Я вижу неожиданные результаты при попытке чтения в формате PNG из нескольких архивов для страницы файлового браузера.
URL-адреса документов запрашиваются в фоновом режиме, а затем создается объект данных файла. После завершения запроса в основном потоке вызывается обновление пользовательского интерфейса, и объекты данных файла действуют как поставщик данных для представления коллекции.
PNG сериализуется в Data следующим образом:
let imageData = UIImagePNGRepresentation(image)
Когда данные считываются, соответствующие записи извлекаются в память и затем десериализуются в соответствующие объекты.
Я вижу, что эскизы документов отображаются только периодически. Возможно ли, что ZIPFoundation не является потокобезопасным для фоновых операций?
Я смоделировал это в более простом тестовом проекте, он работает нормально, но я не читаю несколько архивов и не вызываю это в фоновом режиме. Я что-то делаю не так (код ниже)?
Использует ли закрытие потребителя свой собственный поток (и, возможно, я возвращаю данные до его завершения)?
do {
let decoder = JSONDecoder()
let archive = Archive(url: URL, accessMode: .read)
// Metadata
if let metadataData = try archive?.readData(named: Document.MetadataFilename) {
self.metadata = try decoder.decode(Metadata.self, from: metadataData)
} else {
logDebug(self, "metadata not read")
}
// Preview
if let previewData = try archive?.readData(named: Document.PreviewFilename) {
if let image = UIImage(data: previewData) {
self.image = image
return
}
} else {
logDebug(self, "image not read")
}
} catch {
logError(self, "Loading of FileWrapper failed with error: \(error.localizedDescription)")
}
// Failure fall through
// Mark this as failed by using the x image
self.image = UIImage(named: "unavailable")
}
Мое расширение архива для удобства:
/// Associates optional data with entry name
struct NamedData {
let name : String
let data : Data?
}
// MARK: - Private
extension Archive {
private func addData(_ entry: NamedData) throws {
let archive = self
let name = entry.name
let data = entry.data
do {
if let data = data {
try archive.addEntry(with: name, type: .file, uncompressedSize: UInt32(data.count), compressionMethod: .none, provider: { (position, size) -> Data in
return data
})
}
} catch {
throw error
}
}
private func removeData(_ entry: NamedData) throws {
let archive = self
let name = entry.name
do {
if let entry = archive[name] { try archive.remove(entry) }
} catch {
throw error
}
}
}
// MARK: - Public
extension Archive {
/// Update NamedData entries in the archive
func updateData(entries: [NamedData]) throws {
// Walk each entry and overwrite
do {
for entry in entries {
try removeData(entry)
try addData(entry)
}
} catch {
throw error
}
}
/// Add NamedData entries to the archive (updateData is the preferred
/// method since no harm comes from removing entries before adding them)
func addData(entries: [NamedData]) throws {
// Walk each entry and create
do {
for entry in entries {
try addData(entry)
}
} catch {
throw error
}
}
/// Read Data out of the entry using its name
func readData(named name: String) throws -> Data? {
let archive = self
// Get data from entry
do {
var entryData : Data? = nil
if let entry = archive[name] {
// _ = returned checksum
let _ = try archive.extract(entry, consumer: { (data) in
entryData = data
})
}
return entryData
} catch {
throw error
}
}
}
Похоже, может быть проблема с размером файла изображения. Если я уменьшу изображение, оно будет нормально храниться и загружаться. Я также попытался добавить sleep () к генерации миниатюр, чтобы исключить вероятность того, что причиной является время создания миниатюр (а не размер данных изображения). Пока что кажется, что если изображение имеет> какое-то значение, оно не может правильно записать в архив.
API-интерфейсы на основе замыкания в ZIPFoundation предназначены для предоставления / использования данных по частям. В зависимости от окончательного размера ваших данных и настроенного размера блока (необязательный параметр, по умолчанию - 16 * 1024) замыкания поставщика / потребителя могут вызываться несколько раз.
Когда вы извлекаете запись через
let _ = try archive.extract(entry, consumer: { (data) in
entryData = data
})
вы всегда перезаписываете entryData
последним фрагментом, предоставленным замыканием consumer
(если окончательный размер больше, чем размер фрагмента).
Вместо этого вы можете использовать
var entryData = Data()
let _ = try archive.extract(entry, consumer: { (data) in
entryData.append(data)
})
чтобы убедиться, что вся запись накапливается в объекте entryData
.
То же самое происходит в вашем коде Provider
. Вместо того, чтобы всегда возвращать весь объект данных изображения, вы должны предоставлять фрагмент (начиная с position
с size
) каждый раз, когда вызывается закрытие.
Спасибо, Томас! И для полноты: код для фрагментации данных поставщика - let range = Range(position ..< position + size)
return data.subdata(in: range)
У меня была такая же проблема, у меня работало следующее:
archive.addEntry(with: "entryName",
type: .file,
uncompressedSize: dataSize,
compressionMethod: .none,
provider: { (position, size) in
return data.subdata(in: (position ..< position + size))})
Также стоит отметить: я попытался проверить zip-файл на своем Mac (при работе в симуляторе), и мне кажется, что я не могу его расширить (я получаю код ошибки 1).