Есть ли практический способ сжать NSData?

Я не видел документации по этой теме, но это не значит, что ее не существует.


person eric.mitchell    schedule 08.12.2011    source источник
comment
какой тип сжатия? прямая молния? Вы можете взять из него байты и использовать zip-библиотеку   -  person DanZimm    schedule 08.12.2011
comment
Этот вопрос абсолютно эквивалентен тому, можно ли сжимать данные? и ответ, очевидно, положительный.   -  person Lily Ballard    schedule 08.12.2011
comment
возможный дубликат API сжатия на iPhone   -  person Brad Larson    schedule 09.12.2011


Ответы (7)


Да, сжимайте данные с помощью zlib.

@Brad Larson написал об этом: см. Здесь и также добавил код.

Существует CocoaPod, который использует Objective-Zip от flyingdolphinstudio.

person zaph    schedule 08.12.2011
comment
Как кому-то удалось прочитать эту страницу NSDataCategory (cocoadev.com/wiki/NSDataCategory)? Они вообще форматируют этот код? - person jkcl; 05.07.2012

Следуя сообщениям @Zaph и @Brad Larson, ниже приведены 2 метода gzipInflate и gzipDeflate, которые отлично работают для сжатия / распаковки NSData. (код переформатирован из cocoadev.com/wiki/NSDataCategory

#import "zlib.h"
// don't forget to add libz.1.2.x.dylib into your project

- (NSData *)gzipInflate:(NSData*)data
{
    if ([data length] == 0) return data;

    unsigned full_length = [data length];
    unsigned half_length = [data length] / 2;

    NSMutableData *decompressed = [NSMutableData dataWithLength: full_length + half_length];
    BOOL done = NO;
    int status;

    z_stream strm;
    strm.next_in = (Bytef *)[data bytes];
    strm.avail_in = [data length];
    strm.total_out = 0;
    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;

    if (inflateInit2(&strm, (15+32)) != Z_OK) return nil;
    while (!done)
    {
        // Make sure we have enough room and reset the lengths.
        if (strm.total_out >= [decompressed length])
            [decompressed increaseLengthBy: half_length];
        strm.next_out = [decompressed mutableBytes] + strm.total_out;
        strm.avail_out = [decompressed length] - strm.total_out;

        // Inflate another chunk.
        status = inflate (&strm, Z_SYNC_FLUSH);
        if (status == Z_STREAM_END) done = YES;
        else if (status != Z_OK) break;
    }
    if (inflateEnd (&strm) != Z_OK) return nil;

    // Set real length.
    if (done)
    {
        [decompressed setLength: strm.total_out];
        return [NSData dataWithData: decompressed];
    }
    else return nil;
}

- (NSData *)gzipDeflate:(NSData*)data
{
    if ([data length] == 0) return data;

    z_stream strm;

    strm.zalloc = Z_NULL;
    strm.zfree = Z_NULL;
    strm.opaque = Z_NULL;
    strm.total_out = 0;
    strm.next_in=(Bytef *)[data bytes];
    strm.avail_in = [data length];

    // Compresssion Levels:
    //   Z_NO_COMPRESSION
    //   Z_BEST_SPEED
    //   Z_BEST_COMPRESSION
    //   Z_DEFAULT_COMPRESSION

    if (deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY) != Z_OK) return nil;

    NSMutableData *compressed = [NSMutableData dataWithLength:16384];  // 16K chunks for expansion

    do {

        if (strm.total_out >= [compressed length])
            [compressed increaseLengthBy: 16384];

        strm.next_out = [compressed mutableBytes] + strm.total_out;
        strm.avail_out = [compressed length] - strm.total_out;

        deflate(&strm, Z_FINISH);  

    } while (strm.avail_out == 0);

    deflateEnd(&strm);

    [compressed setLength: strm.total_out];
    return [NSData dataWithData:compressed];
}

Из журнала:

[data length] (orig):989631
[data length] (gz):  102757
[data length] (ungz):989631
person Community    schedule 09.07.2012
comment
@Rickay, если вы хотите использовать bzip2 вместо gzip, см. этот пост. - person lucasart; 09.07.2012
comment
Выглядит очень красиво, но при компиляции Xcode говорит, что _BZ2_bzCompressEnd и его друзья являются неопределенными символами для armv7. - person eric.mitchell; 09.07.2012
comment
Не уверен, что здесь не так с настройкой. Помимо добавления #import "bzlib.h" и проверки того, что библиотека libbz2.1.x.dylib добавлена ​​на этапах сборки - ›Связать двоичный файл с библиотеками, он должен хорошо скомпилироваться. - person lucasart; 10.07.2012
comment
Ох ... добавьте -lbz2 в Build Settings - ›Other Linker Flags. Это должно сработать. - person lucasart; 10.07.2012
comment
Хорошая работа, собрать все вместе. В моем случае все, что мне нужно было добавить libz.dylib. - person user523234; 16.07.2012
comment
Я использовал это для сжатия NSDATA, но я получаю журнал - person Vipin Vijay; 24.02.2013
comment
Пожалуйста, помогите - я пытаюсь сохранить MPMediaItem на сервере, сжав его с помощью zlib (ваш код выше). Однако длина уменьшается только с 15313956 до 14805754. Требуется намного больше сжатия. Я изменил MPMediaItem на NSData с помощью кода по этой ссылке: stackoverflow.com/questions/18824983/ - person Badi8beach; 31.08.2014
comment
Я потратил много времени, пытаясь понять это. Насколько я понимаю, это должно работать. Кроме того, компоновщик жалуется, что lseek из gzlib.c и некоторых других, таких как read и write, недействителен. Но, судя по блогам, которые я просматривал, это не должно быть проблемой. Любая помощь по этому поводу очень ценится. - person Badi8beach; 31.08.2014
comment
Обратите внимание, что в этом ответе есть обновленный метод inflate для лучшей поддержки 64-битной версии: stackoverflow.com/a/17822217/449161 - person Ben Flynn; 15.12.2014
comment
Привет ... я использую Gzip для сжатия больших строковых данных .... есть ли проблемы с производительностью с Gzip? Zip лучше, чем Gzip? - person H Raval; 07.03.2017

Начиная с iOS 9.0, есть встроенная поддержка еще нескольких алгоритмов сжатия. Библиотека называется libcompression и поддерживает LZ4, LZMA, ZLIB и LZFSE.

Вот пример Swift использования libcompression для распаковки LZMA. Он подробный, но позволяет избежать внешних зависимостей и может быть скрыт в расширении на NSData.

import Compression

let streamPtr = UnsafeMutablePointer<compression_stream>.alloc(1)
var stream = streamPtr.memory
var status: compression_status

status = compression_stream_init(&stream, COMPRESSION_STREAM_DECODE, COMPRESSION_LZMA)
stream.src_ptr = UnsafePointer<UInt8>(compressedData.bytes)
stream.src_size = compressedData.length

let dstBufferSize: size_t = 4096
let dstBufferPtr = UnsafeMutablePointer<UInt8>.alloc(dstBufferSize)
stream.dst_ptr = dstBufferPtr
stream.dst_size = dstBufferSize

let decompressedData = NSMutableData()

repeat {
    status = compression_stream_process(&stream, 0)
    switch status {
    case COMPRESSION_STATUS_OK:
        if stream.dst_size == 0 {
            decompressedData.appendBytes(dstBufferPtr, length: dstBufferSize)
            stream.dst_ptr = dstBufferPtr
            stream.dst_size = dstBufferSize
        }
    case COMPRESSION_STATUS_END:
        if stream.dst_ptr > dstBufferPtr {
            decompressedData.appendBytes(dstBufferPtr, length: stream.dst_ptr - dstBufferPtr)
        }
    default:
        break
    }
}
while status == COMPRESSION_STATUS_OK

compression_stream_destroy(&stream)

if status == COMPRESSION_STATUS_END {
    // Decompression succeeded, do something with decompressedData
}
else {
    // Decompression failed
}
person jbg    schedule 24.12.2015
comment
Расширение Swift для NSData можно найти здесь: github.com/leemorgan/NSData-Compression - person Klaas; 25.06.2016

Я сделал хороший интерфейс сжатия Objective-C BZip2, доступный как CocoaPod: https://github.com/blakewatters/BZipCompression

person Blake Watters    schedule 19.09.2013

Готовая оболочка Swift 3 для libcompression. https://github.com/mw99/DataCompression

Оболочка Swift libcompression как расширение для типа данных (ZLIB, LZFSE, LZMA, LZ4, deflate, RFC-1950, RFC-1951)

Интересно поиграть на детской площадке: коэффициент сжатия детской площадки

person LimeRed    schedule 08.12.2016

В iOS 13 и macOS 10.15 или новее вы можете использовать новый compressed метод NSData:

let compressedData = try? NSData(data: data).compressed(using: .zlib)

К сожалению, этот метод не был перенесен в собственный Data класс Swift, но NSData можно просто преобразовать в Data, добавив as Data после строки кода выше.

person Ely    schedule 26.01.2020
comment
если данные предназначены для изображения, они не будут сжаты с помощью этого метода - person JAHelia; 07.03.2020
comment
@JAHelia Может быть одним из тех недокументированных средств защиты Apple разработчика от его собственных плохих идей. - person Ely; 07.03.2020

Попробуйте следующее: https://github.com/mattt/Godzippa Это было полезно для меня.

person Nuzhdin Vladimir    schedule 16.10.2015