ZIPFoundation: проблема с записью больших файлов PNG в архив через поставщика данных

ОБНОВЛЕНИЕ: я могу воспроизвести это, установив размер 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
        }
    }
}

Также стоит отметить: я попытался проверить zip-файл на своем Mac (при работе в симуляторе), и мне кажется, что я не могу его расширить (я получаю код ошибки 1).

joshd 01.05.2018 23:53

Похоже, может быть проблема с размером файла изображения. Если я уменьшу изображение, оно будет нормально храниться и загружаться. Я также попытался добавить sleep () к генерации миниатюр, чтобы исключить вероятность того, что причиной является время создания миниатюр (а не размер данных изображения). Пока что кажется, что если изображение имеет> какое-то значение, оно не может правильно записать в архив.

joshd 02.05.2018 09:26
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
3
2
216
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

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)

joshd 03.05.2018 06:13

У меня была такая же проблема, у меня работало следующее:

archive.addEntry(with: "entryName",
                 type: .file,
                 uncompressedSize: dataSize,
                 compressionMethod: .none,
                 provider: { (position, size) in
                     return data.subdata(in: (position ..< position + size))})

Другие вопросы по теме