data数据压缩 ios_ios 数据库添加中data数据的大小 - CSDN
  • 一、客户端-服务端数据压缩解压流程 客户端生成request,设置header允许使用压缩("Accept-Encoding","gzip"),即是告诉服务器,客户端支持压缩,但凡可以压缩的服务器,尽管来吧!服务器收到这个header,如果它支持...

    一、客户端-服务端数据压缩解压流程

    客户端生成request,设置header允许使用压缩("Accept-Encoding","gzip"),即是告诉服务器,客户端支持压缩,但凡可以压缩的服务器,尽管来吧!服务器收到这个header,如果它支持压缩,可以通过压缩方式输出数据,然后再写入response的header("Content-Encoding","gzip")

    1.以ASIHttpRequest为例,代码如下:

        <span style="font-size:14px;">NSURL* requestURL = [NSURL URLWithString:_listURL];
        ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:requestURL];
        // 默认为YES, 你可以设定它为NO来禁用gzip压缩
        [request setAllowCompressedResponse:YES];
        [request setDelegate:self];
        [request startAsynchronous];
        如果是普通的URLRequest,只要: request.setHeader("Accept-Encoding","gzip");</span>
    2.服务器端返回:response.setHeader("Content-Encoding","gzip");

     libz库
      libz库是官方的一个库,貌似ASIHttpRequest也是用这个库解压的,当我们获得压缩过的data数据以后(方法与上面类似,只是获得    了  

     普通的data数据响应),可以使用这种方法解压数据,解压方法如下所示

     数据压缩参考:http://www.clintharris.net/2009/how-to-gzip-data-in-memory-using-objective-c/

     数据解压缩参考:ASIHttpRequest库的文件:ASIDataDecompressor.m

    // 头文件:GzipUtility.h
    
    #import <Foundation/Foundation.h>
    
    @interface GzipUtility : NSObject
    
            // 数据压缩
    
            + (NSData *)compressData:(NSData*)uncompressedData;  
    
            // 数据解压缩
    
            + (NSData *)decompressData:(NSData *)compressedData;
    
    @end
    
    
    // 实现文件:GzipUtility.m
    
    #import "GzipUtility.h"
    
    #import "zlib.h"
    
    @implementation GzipUtility
    
    +(NSData*) compressData: (NSData*)uncompressedData  {  
    
        /* 
    
         Special thanks to Robbie Hanson of Deusty Designs for sharing sample code 
    
         showing how deflateInit2() can be used to make zlib generate a compressed 
    
         file with gzip headers: 
    
         http://deusty.blogspot.com/2007/07/gzip-compressiondecompression.html 
    
         */  
    
        
    
        if (!uncompressedData || [uncompressedData length] == 0)  {  
    
            NSLog(@"%s: Error: Can't compress an empty or null NSData object.", __func__);  
    
            return nil;  
    
        }  
    
        
    
        /* Before we can begin compressing (aka "deflating") data using the zlib 
    
         functions, we must initialize zlib. Normally this is done by calling the 
    
         deflateInit() function; in this case, however, we'll use deflateInit2() so 
    
         that the compressed data will have gzip headers. This will make it easy to 
    
         decompress the data later using a tool like gunzip, WinZip, etc. 
    
         
    
         deflateInit2() accepts many parameters, the first of which is a C struct of 
    
         type "z_stream" defined in zlib.h. The properties of this struct are used to 
    
         control how the compression algorithms work. z_stream is also used to 
    
         maintain pointers to the "input" and "output" byte buffers (next_in/out) as 
    
         well as information about how many bytes have been processed, how many are 
    
         left to process, etc. */  
    
        z_stream zlibStreamStruct;  
    
        zlibStreamStruct.zalloc    = Z_NULL; // Set zalloc, zfree, and opaque to Z_NULL so  
    
        zlibStreamStruct.zfree     = Z_NULL; // that when we call deflateInit2 they will be  
    
        zlibStreamStruct.opaque    = Z_NULL; // updated to use default allocation functions.  
    
        zlibStreamStruct.total_out = 0; // Total number of output bytes produced so far  
    
        zlibStreamStruct.next_in   = (Bytef*)[uncompressedData bytes]; // Pointer to input bytes  
    
        zlibStreamStruct.avail_in  = [uncompressedData length]; // Number of input bytes left to process  
    
        
    
        /* Initialize the zlib deflation (i.e. compression) internals with deflateInit2(). 
    
         The parameters are as follows: 
    
         
    
         z_streamp strm - Pointer to a zstream struct 
    
         int level      - Compression level. Must be Z_DEFAULT_COMPRESSION, or between 
    
         0 and 9: 1 gives best speed, 9 gives best compression, 0 gives 
    
         no compression. 
    
         int method     - Compression method. Only method supported is "Z_DEFLATED". 
    
         int windowBits - Base two logarithm of the maximum window size (the size of 
    
         the history buffer). It should be in the range 8..15. Add 
    
         16 to windowBits to write a simple gzip header and trailer 
    
         around the compressed data instead of a zlib wrapper. The 
    
         gzip header will have no file name, no extra data, no comment, 
    
         no modification time (set to zero), no header crc, and the 
    
         operating system will be set to 255 (unknown). 
    
         int memLevel   - Amount of memory allocated for internal compression state. 
    
         1 uses minimum memory but is slow and reduces compression 
    
         ratio; 9 uses maximum memory for optimal speed. Default value 
    
         is 8. 
    
         int strategy   - Used to tune the compression algorithm. Use the value 
    
         Z_DEFAULT_STRATEGY for normal data, Z_FILTERED for data 
    
         produced by a filter (or predictor), or Z_HUFFMAN_ONLY to 
    
         force Huffman encoding only (no string match) */  
    
        int initError = deflateInit2(&zlibStreamStruct, Z_DEFAULT_COMPRESSION, Z_DEFLATED, (15+16), 8, Z_DEFAULT_STRATEGY);  
    
        if (initError != Z_OK)  
    
        {  
    
            NSString *errorMsg = nil;  
    
            switch (initError)  
    
            {  
    
                case Z_STREAM_ERROR:  
    
                    errorMsg = @"Invalid parameter passed in to function.";  
    
                    break;  
    
                case Z_MEM_ERROR:  
    
                    errorMsg = @"Insufficient memory.";  
    
                    break;  
    
                case Z_VERSION_ERROR:  
    
                    errorMsg = @"The version of zlib.h and the version of the library linked do not match.";  
    
                    break;  
    
                default:  
    
                    errorMsg = @"Unknown error code.";  
    
                    break;  
    
            }  
    
            NSLog(@"%s: deflateInit2() Error: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);  
    
            [errorMsg release];  
    
            return nil;  
    
        }  
    
        
    
        // Create output memory buffer for compressed data. The zlib documentation states that  
    
        // destination buffer size must be at least 0.1% larger than avail_in plus 12 bytes.  
    
        NSMutableData *compressedData = [NSMutableData dataWithLength:[uncompressedData length] * 1.01 + 12];  
    
        
    
        int deflateStatus;  
    
        do  
    
        {  
    
            // Store location where next byte should be put in next_out  
    
            zlibStreamStruct.next_out = [compressedData mutableBytes] + zlibStreamStruct.total_out;  
    
            
    
            // Calculate the amount of remaining free space in the output buffer  
    
            // by subtracting the number of bytes that have been written so far  
    
            // from the buffer's total capacity  
    
            zlibStreamStruct.avail_out = [compressedData length] - zlibStreamStruct.total_out;  
    
            
    
            /* deflate() compresses as much data as possible, and stops/returns when 
    
             the input buffer becomes empty or the output buffer becomes full. If 
    
             deflate() returns Z_OK, it means that there are more bytes left to 
    
             compress in the input buffer but the output buffer is full; the output 
    
             buffer should be expanded and deflate should be called again (i.e., the 
    
             loop should continue to rune). If deflate() returns Z_STREAM_END, the 
    
             end of the input stream was reached (i.e.g, all of the data has been 
    
             compressed) and the loop should stop. */  
    
            deflateStatus = deflate(&zlibStreamStruct, Z_FINISH);  
    
            
    
        } while ( deflateStatus == Z_OK );        
    
        
    
        // Check for zlib error and convert code to usable error message if appropriate  
    
        if (deflateStatus != Z_STREAM_END)  
    
        {  
    
            NSString *errorMsg = nil;  
    
            switch (deflateStatus)  
    
            {  
    
                case Z_ERRNO:  
    
                    errorMsg = @"Error occured while reading file.";  
    
                    break;  
    
                case Z_STREAM_ERROR:  
    
                    errorMsg = @"The stream state was inconsistent (e.g., next_in or next_out was NULL).";  
    
                    break;  
    
                case Z_DATA_ERROR:  
    
                    errorMsg = @"The deflate data was invalid or incomplete.";  
    
                    break;  
    
                case Z_MEM_ERROR:  
    
                    errorMsg = @"Memory could not be allocated for processing.";  
    
                    break;  
    
                case Z_BUF_ERROR:  
    
                    errorMsg = @"Ran out of output buffer for writing compressed bytes.";  
    
                    break;  
    
                case Z_VERSION_ERROR:  
    
                    errorMsg = @"The version of zlib.h and the version of the library linked do not match.";  
    
                    break;  
    
                default:  
    
                    errorMsg = @"Unknown error code.";  
    
                    break;  
    
            }  
    
            NSLog(@"%s: zlib error while attempting compression: \"%@\" Message: \"%s\"", __func__, errorMsg, zlibStreamStruct.msg);  
    
            [errorMsg release];  
    
            
    
            // Free data structures that were dynamically created for the stream.  
    
            deflateEnd(&zlibStreamStruct);  
    
            
    
            return nil;  
    
        }  
    
        
    
        // Free data structures that were dynamically created for the stream.  
    
        deflateEnd(&zlibStreamStruct);  
    
        
    
        [compressedData setLength: zlibStreamStruct.total_out];  
    
        
    
        return compressedData;  
    
    }  
    
    
    + (NSData *)decompressData:(NSData *)compressedData {
    
        z_stream zStream;
    
        zStream.zalloc = Z_NULL;
    
        zStream.zfree = Z_NULL;
    
        zStream.opaque = Z_NULL;
    
        zStream.avail_in = 0;
    
        zStream.next_in = 0;
    
        int status = inflateInit2(&zStream, (15+32));
    
        
    
        if (status != Z_OK) {
    
            return nil;
    
        }
    
        
    
        Bytef *bytes = (Bytef *)[compressedData bytes];
    
        NSUInteger length = [compressedData length];
    
        
    
        NSUInteger halfLength = length/2;
    
        NSMutableData *uncompressedData = [NSMutableData dataWithLength:length+halfLength];
    
        
    
        zStream.next_in = bytes;
    
        zStream.avail_in = (unsigned int)length;
    
        zStream.avail_out = 0;
    
        
    
        NSInteger bytesProcessedAlready = zStream.total_out;
    
        while (zStream.avail_in != 0) {
    
            
    
            if (zStream.total_out - bytesProcessedAlready >= [uncompressedData length]) {
    
                [uncompressedData increaseLengthBy:halfLength];
    
            }
    
            
    
            zStream.next_out = (Bytef*)[uncompressedData mutableBytes] + zStream.total_out-bytesProcessedAlready;
    
            zStream.avail_out = (unsigned int)([uncompressedData length] - (zStream.total_out-bytesProcessedAlready));
    
            
    
            status = inflate(&zStream, Z_NO_FLUSH);
    
            
    
            if (status == Z_STREAM_END) {
    
                break;
    
            } else if (status != Z_OK) {
    
                return nil;
    
            }
    
        }
    
        
    
        status = inflateEnd(&zStream);
    
        if (status != Z_OK) {
    
            return nil;
    
        }
    
        
    
        [uncompressedData setLength: zStream.total_out-bytesProcessedAlready];  // Set real length
    
    
        return uncompressedData;    
    
    }
    
    
    @end


    展开全文
  • 最近遇到一个图片压缩的问题,项目需求压缩图片500k以内上传服务器,还要求图片要清晰一点。还有证明是图片500k已经确实很清晰了,那就没办法,做呗~~!(不喜欢听bb的可以直接去下面撸代码) 思路 本来以为很...

    大家好,好久没有更新博客了。一个早9晚5点半的硬是上成了996。悲剧的加班狗!

    背景:

            最近遇到一个图片压缩的问题,项目需求压缩图片500k以内上传服务器,还要求图片要清晰一点。还有证明是图片500k已经确实很清晰了,那就没办法,做呗~~!(不喜欢听bb的可以直接去下面撸代码)

    思路

            本来以为很简单的问题,自己随意写了一个UIImageJPEGRepresentation的方法进行一个循环压缩不就搞定了?,后来事实证明这个玩意儿很坑,有太多东西不是你想当然的。

    1.为什么不提UIImagePNGRepresentation(<#UIImage * _Nonnull image#>)?

       回复:据说这个读取图片的大小会比较大,因为是png格式,读取的内容会有多图层的的问题导致读取的会显示比较大,而且比较耗时间。网上有人做过测试:同样是读取摄像头拍摄的同样景色的照片,UIImagePNGRepresentation() 返回的数据量大小为199K,而 UIImageJPEGRepresentation(UIImage* image, 1.0) 返回的数据量大小只为 140KB,比前者少了50多KB。如果对图片的清晰度要求不高,还可以通过设置 UIImageJPEGRepresentation 函数的第二个参数,大幅度降低图片数据量。
    如果还有什么问题可以继续百度,这里不进行过多赘述。

    2.首先第一个参数是我们都知道的图片image,但是第二个参数scale,一个0~1的浮点型比率,你以为0就是没有,压缩到0b大小,1.0就是原图大小?答案是?:错,首先你的图片的大小是根据(图片的宽*图片的高*每一个色彩的深度,这个和手机的系统有关,一般是4)。你的图片只会按照你的手机像素的分辨率[UIScreen mainScreen].scale来读取值。其次,第二个参数苹果官方并没有明确说明这个参数的具体意义。对于大图片来说,即使你的scale选的很小,比如:0.0000000(n个0)001,但是得到的结果还是很大,这里做了一个实验:一个10M左右的图片,处理后大小为2M多。有点像是“压不动”的感觉。当然如果是小图片的话那就是没问题,能满足你的希望的压缩到的大小。

            既然是循环压,那么就要说一下算法,考虑到递归,二分法,后来发现网上也是有的,二分法处理。更快一点压缩图片到指定的大小。先看一段代码:

    //二分最大10次,区间范围精度最大可达0.00097657;最大6次,精度可达0.015625
            for (int i = 0; i < 10; ++i) {
                compression = (max + min) / 2;
                imageData = UIImageJPEGRepresentation(image, compression);
                //容错区间范围0.9~1.0
                if (imageData.length < fImageBytes * 0.9) {
                    min = compression;
                } else if (imageData.length > fImageBytes) {
                    max = compression;
                } else {
                    break;
                }
            }

    上面就是使用二分法进行处理,比for循环依次递减“高效”很多,而且也合理很多。

     

    但是你也会问,压缩“压不动”怎么办?

    这样压缩到“极致”(一般我们不用进行太多的for循环,个人觉得参数到0.05已经可以了如果还是比你想要的大很多那就不要用UIImageJPEGRepresentation了),劳民伤财,劳的是cpu的高速运转,伤的是手机老化加快。哈哈,皮一下!

    然后我们其实可以换一个方式,进行尺寸压缩:

    提到尺寸压缩,你会不会很失望,看你的文章,原来也是使用UIGraphicsBeginImageContextWithOptions然后drawInRect绘制一个图片,大小。代码类似如下:

    /* 根据 dWidth dHeight 返回一个新的image**/
    - (UIImage *)drawWithWithImage:(UIImage *)imageCope Size:(CGSize)size {
    //这里设置为0,意为自动设置清晰度,图片可以是别的传过来的图片信息
        UIGraphicsBeginImageContextWithOptions(size, NO,0);
        [imageCope drawInRect:CGRectMake(0, 0, size.width, size.height)];
        imageCope = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        return imageCope;
    }

            首先我需要说一下这个绘制很耗内存性能的,[UIImage drawInRect:]在绘制时,先解码图片,再生成原始分辨率大小的bitmap,这是很耗内存的,并且还有位数对齐等耗时操作。如果在一个方法中循环压缩比例进行代码的比例压缩,那么这种使用UIKit类进行图片绘制的话是需要先把图片读入内存然后在进行绘制,那么势必会给内存中占用大量的临时内存bitmap,而这个如果再加上循环,那么内存占有将是不可估量的。

    你可能会说,我加一个自动释放池@autoreleasepool,不就好了?

            错:首先这个自动释放池@autoreleasepool不要放在循环的外面,包着这个循环,原因就不过多说明,可以自行百度。然后放在for循环内部包着这个绘制的方法,你的内存并不是画完就得到了释放,内存占有的情况可以得到缓解,但是还是不能解决内存突然暴增的问题。尤其是大图片的压缩尤其明显。

             然后你会想换一个方式,这里我也亲测试了,使用Image I/O相关的处理方式,使用相关的生成缩略图的形式压缩图片文件。直接上代码如下:

    记得导入相关的头文件
    #import <AssetsLibrary/ALAsset.h>
    #import <AssetsLibrary/ALAssetRepresentation.h>
    
    
    static size_t getAssetBytesCallback(void *info, void *buffer, off_t position, size_t count{
        ALAssetRepresentation *rep = (__bridge id)info;
    
        NSError *error = nil;
    
        size_t countRead = [rep getBytes:(uint8_t *)buffer fromOffset:position length:count error:&error];
    
        if (countRead == 0 && error) {
            // We have no way of passing this info back to the caller, so we log it, at least.
    
            NSLog(@"thumbnailForAsset:maxPixelSize: got an error reading an asset: %@", error);
        }
        return countRead;
    }
    
    static void releaseAssetCallback(void *info) {
    
        // The info here is an ALAssetRepresentation which we CFRetain in thumbnailForAsset:maxPixelSize:.
    
        // This release balances that retain.
        CFRelease(info);
    }
    
    
    - (UIImage *)thumbnailForAsset:(ALAsset *)asset maxPixelSize:(NSUInteger)size {
        NSParameterAssert(asset != nil);
        NSParameterAssert(size > 0);
    
        ALAssetRepresentation *rep = [asset defaultRepresentation];
        CGDataProviderDirectCallbacks callbacks = {
            .version = 0,
            .getBytePointer = NULL,
            .releaseBytePointer = NULL,
            .getBytesAtPosition = getAssetBytesCallback,
            .releaseInfo = releaseAssetCallback,
        };
        CGDataProviderRef provider = CGDataProviderCreateDirect((void *)CFBridgingRetain(rep), [rep size], &callbacks);
        CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
        
        CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) @{
                                                                                                          (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                                                                          (NSString *)kCGImageSourceThumbnailMaxPixelSize : @(size),
                                                                                                          (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                                                                          });
        CFRelease(source);
        CFRelease(provider);
        
        if (!imageRef) {
            return nil;
        }
        
        UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
        
        CFRelease(imageRef);
        
        return toReturn;
    }

    这个是网上的,说的不清楚,(某两个人以及阿里云文档)只管代码补上,很反感。而且还有人搞了两个voidvoid,都是什么,,,,,自己搞了一下,这个东西,可以结合从数据库获取的info返回使用。这里代码如下:

    #pragma mark - UIImagePickerControllerDelegate
    - (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary *)info {
        NSLog(@"info:\n%@", info);
        UIImage *image = info[UIImagePickerControllerOriginalImage];
        NSData *imgData = UIImageJPEGRepresentation(image, 1.0);
        NSLog(@"length1: %lu", (unsigned long)imgData.length);
        
        NSURL *imageURL = info[UIImagePickerControllerReferenceURL];
        ALAssetsLibrary *assetsLibrary = [[ALAssetsLibrary alloc] init];
        [assetsLibrary assetForURL:imageURL resultBlock:^(ALAsset *asset) {
            image = [self thumbnailForAsset:asset maxPixelSize:600];
            imgData = UIImageJPEGRepresentation(image, 1.0);
            NSLog(@"length2: %lu", (unsigned long)imgData.length);
            NSArray * paths=NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
            NSString *filePath = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"new/ceshi.jpg"];// 保存文件的名称
            
            BOOL result = [imgData writeToFile: filePath atomically:YES]; // 保存成功会返回YES
            
            NSLog(@"文件保存成功?%d",result);
        } failureBlock:nil];
        
        [picker dismissViewControllerAnimated:YES completion:^{
        }];
    }

    如果要是问我怎么打开相册?这里也配给你们:

     //初始化UIImagePickerController类
        UIImagePickerController * picker = [[UIImagePickerController alloc] init];
        //判断数据来源为相册
        picker.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
        //设置代理
        picker.delegate = self;
        //打开相册
        [self presentViewController:picker animated:YES completion:nil];

    记得声明一下代理。

    使用ImageIO接口,避免在改变图片大小的过程中产生临时的bitmap,就能够在很大程度上减少内存的占有从而避免由此导致的app闪退问题。

    在这之前我也直接有过另外一种方式压缩图片(直接靠图片尺寸压缩绘制图片):

    + (void)compressedImageFiles:(UIImage *)image imageKB:(CGFloat)fImageKBytes imageBlock:(ReturnCompressImage)block {
        
        __block UIImage *imageCope = image;
        CGFloat fImageBytes = fImageKBytes * 1024;//需要压缩的字节Byte
        __block NSData *uploadImageData = nil;
        
    //        uploadImageData = UIImagePNGRepresentation(imageCope);
        uploadImageData = UIImageJPEGRepresentation(imageCope, 1.0);
    //    NSLog(@"图片压前缩成 %fKB",uploadImageData.length/1024.0);
    //    CGFloat value1 = uploadImageData.length/1024.0;
    
        CGSize size = imageCope.size;
        CGFloat imageWidth = size.width;
        CGFloat imageHeight = size.height;
        
        if (uploadImageData.length > fImageBytes && fImageBytes >0) {
            
            dispatch_async(dispatch_queue_create("CompressedImage", DISPATCH_QUEUE_SERIAL), ^{
                
                /* 宽高的比例 **/
                CGFloat ratioOfWH = imageWidth/imageHeight;
                /* 压缩率 **/
                CGFloat compressionRatio = fImageBytes/uploadImageData.length;
                /* 宽度或者高度的压缩率 **/
                CGFloat widthOrHeightCompressionRatio = sqrt(compressionRatio);
                
                CGFloat dWidth   = imageWidth *widthOrHeightCompressionRatio;
                CGFloat dHeight  = imageHeight*widthOrHeightCompressionRatio;
                if (ratioOfWH >0) { /* 宽 > 高,说明宽度的压缩相对来说更大些 **/
                    dHeight = dWidth/ratioOfWH;
                }else {
                    dWidth  = dHeight*ratioOfWH;
                }
                
                imageCope = [self drawWithWithImage:imageCope width:dWidth height:dHeight];
                //            uploadImageData = UIImagePNGRepresentation(imageCope);
                uploadImageData = UIImageJPEGRepresentation(imageCope, 1.0);
                
    //            NSLog(@"当前的图片已经压缩成 %fKB",uploadImageData.length/1024.0);
                //微调
                NSInteger compressCount = 0;
                /* 控制在 1M 以内**/
                while (fabs(uploadImageData.length - fImageBytes) > 1024) {
                    /* 再次压缩的比例**/
                    CGFloat nextCompressionRatio = 0.9;
                    
                    if (uploadImageData.length > fImageBytes) {
                        dWidth = dWidth*nextCompressionRatio;
                        dHeight= dHeight*nextCompressionRatio;
                    }else {
                        dWidth = dWidth/nextCompressionRatio;
                        dHeight= dHeight/nextCompressionRatio;
                    }
                    
                    imageCope = [self drawWithWithImage:imageCope width:dWidth height:dHeight];
                    //                uploadImageData = UIImagePNGRepresentation(imageCope);
                    uploadImageData = UIImageJPEGRepresentation(imageCope, 1.0);
                    
                    /*防止进入死循环**/
                    compressCount ++;
                    if (compressCount == 10) {
                        break;
                    }
                }
                
    //            NSLog(@"图片已经压缩成 %fKB",uploadImageData.length/1024.0);
    //            CGFloat value2 = uploadImageData.length/1024.0;
    
                imageCope = [[UIImage alloc] initWithData:uploadImageData];
                
                dispatch_sync(dispatch_get_main_queue(), ^{
                    if (block) {
                        block(imageCope);
                    }
                });
            });
        } else{
            if (block) {
                block(imageCope);
            }
        }
    }
    
    /* 根据 dWidth dHeight 返回一个新的image**/
    + (UIImage *)drawWithWithImage:(UIImage *)imageCope width:(CGFloat)dWidth height:(CGFloat)dHeight{
        
        UIGraphicsBeginImageContext(CGSizeMake(dWidth, dHeight));
        [imageCope drawInRect:CGRectMake(0, 0, dWidth, dHeight)];
        imageCope = UIGraphicsGetImageFromCurrentImageContext();
        UIGraphicsEndImageContext();
        
        return imageCope;
        
    }

    这种方式极耗手机的cpu,而且绘制也是使用uikit进行绘制,内存占用也是比较严重的。

    总结:

    综合上面的所有情况现在我的最终处理方案如下:

    1.首先使用UIImageJPEGRepresentation进行尽可能的压缩,这里我使用二分法(考虑到手机性能问题,这里二分法设置10次(能精确到0.00097657)以内即可)处理压缩的比率参数,

    2.首先根据我设置的二分法的最小可能压缩一下原图片信息,比对一下最小的二分法能处理的最大限度得到的最小图片信息能否满足条件(在你设定的目标大小以内)。以减少不必要的循环,保护cpu处理。

    3.然后对处理后的图片信息,保留最大压缩比(即上面的最小二分法的scale结果),然后再进行和最终目标的大小比值,求根,然后对图像的宽和高等比压缩处理。然后再次根据最小二分法的scale以UIImageJPEGRepresentation读取结果再和你的目标大小比对,然后以此循环。直到大小小于目标大小。

    这样得到的图片几乎就能够在你设定的大小以内的附近,而且图片的信息肉眼几乎看不出来多大的区别。亲自试了3M,4M,6M,10M的大图片没有发现内存消耗有太大的波动。而且压缩出来的图片清晰度很高。

    这里上代码如下:

    - (void)compressedImageFiles:(UIImage *)image
                             imageKB:(CGFloat)fImageKBytes imageBlock:(void(^)(NSData *imageData))block{
        //二分法压缩图片
        CGFloat compression = 1;
        NSData *imageData = UIImageJPEGRepresentation(image, compression);
        NSUInteger fImageBytes = fImageKBytes * 1000;//需要压缩的字节Byte,iOS系统内部的进制1000
        if (imageData.length <= fImageBytes){
            block(imageData);
            return;
        }
        CGFloat max = 1;
        CGFloat min = 0;
        //指数二分处理,s首先计算最小值
        compression = pow(2, -6);
        imageData = UIImageJPEGRepresentation(image, compression);
        if (imageData.length < fImageBytes) {
            //二分最大10次,区间范围精度最大可达0.00097657;最大6次,精度可达0.015625
            for (int i = 0; i < 6; ++i) {
                compression = (max + min) / 2;
                imageData = UIImageJPEGRepresentation(image, compression);
                //容错区间范围0.9~1.0
                if (imageData.length < fImageBytes * 0.9) {
                    min = compression;
                } else if (imageData.length > fImageBytes) {
                    max = compression;
                } else {
                    break;
                }
            }
            
            block(imageData);
            return;
        }
    
        // 对于图片太大上面的压缩比即使很小压缩出来的图片也是很大,不满足使用。
        //然后再一步绘制压缩处理
        UIImage *resultImage = [UIImage imageWithData:imageData];
        while (imageData.length > fImageBytes) {
            @autoreleasepool {
                CGFloat ratio = (CGFloat)fImageBytes / imageData.length;
                //使用NSUInteger不然由于精度问题,某些图片会有白边
                NSLog(@">>>>>>>>>>>>>>>>>%f>>>>>>>>>>>>%f>>>>>>>>>>>%f",resultImage.size.width,sqrtf(ratio),resultImage.size.height);
                CGSize size = CGSizeMake((NSUInteger)(resultImage.size.width * sqrtf(ratio)),
                                         (NSUInteger)(resultImage.size.height * sqrtf(ratio)));
    //            resultImage = [self drawWithWithImage:resultImage Size:size];
    //            resultImage = [self scaledImageWithData:imageData withSize:size scale:resultImage.scale orientation:UIImageOrientationUp];
                resultImage = [self thumbnailForData:imageData maxPixelSize:MAX(size.width, size.height)];
                imageData = UIImageJPEGRepresentation(resultImage, compression);
            }
        }
    
    //   整理后的图片尽量不要用UIImageJPEGRepresentation方法转换,后面参数1.0并不表示的是原质量转换。
        block(imageData);
        
    }
    - (UIImage *)thumbnailForData:(NSData *)data maxPixelSize:(NSUInteger)size {
        CGDataProviderRef provider = CGDataProviderCreateWithCFData((__bridge CFDataRef)data);
        CGImageSourceRef source = CGImageSourceCreateWithDataProvider(provider, NULL);
        
        CGImageRef imageRef = CGImageSourceCreateThumbnailAtIndex(source, 0, (__bridge CFDictionaryRef) @{
                                                                                                          (NSString *)kCGImageSourceCreateThumbnailFromImageAlways : @YES,
                                                                                                          (NSString *)kCGImageSourceThumbnailMaxPixelSize : @(size),
                                                                                                          (NSString *)kCGImageSourceCreateThumbnailWithTransform : @YES,
                                                                                                          });
        CFRelease(source);
        CFRelease(provider);
        
        if (!imageRef) {
            return nil;
        }
        
        UIImage *toReturn = [UIImage imageWithCGImage:imageRef];
        
        CFRelease(imageRef);
        
        return toReturn;
    }

    demo地址:https://github.com/KirstenDunst/CSXImageCompress

    demo里面我是做了批量压缩处理,对于多个大图处理,内存也是没有什么太大的波动的。这里附上demo中的批量压缩的图片存储路径:

    如有问题,欢迎指正!

    这里也奉献一些大图(6M,10M)以供测试。:https://pan.baidu.com/s/13eexiBPy_lyJxBLXIddnZw

     

    后续补充:

           之前的测试中有发现使用上面demo中的方法有遇到iphone手机内存不足的手机拍的照片没有问题,再进行compressedImageFiles压缩处理的时候,会得到  “糊掉的”  图片,之后经过处理,在图片进行二分法压缩前,进行了一次图片的重绘操作解决了这个问题。以上的demo工具中的另一个方法resetSizeOfImage:能够解决这个问题。

     

    扩展:

           其实上面的demo中提到的Quartz2D或者UIKit的类中对图片的压缩,水印,剪切等操作,当看过CoreGraphics之后觉得图片原来也可以这么玩。它是iOS的核心图形库,包含Quartz2D绘图API接口,常用的是point,size,rect等这些图形,都定义在这个框架中,类名以CG开头的都属于CoreGraphics框架,它提供的都是C语言函数接口,是可以在iOS和mac OS 通用的。刚接触,这里的了解并不是很深入,但是是更接近底层的图像处理,操作处理上面也是有着很大的灵活性,也有可能会解答iphone内存不足遇到的压缩图片需要重绘问题。之后有时间我会再次整理一篇CoreGraphics的图片处理文章,敬请期待吧!

    展开全文
  • 苹果提供的常用的数据压缩算法LZMA、ZLIB、LZ4等; 这三种算法也是苹果建议的,可跨平台使用; 定义如下: /* Commonly-available encoders */ COMPRESSION_LZ4 = 0x100, // available starting OS X 10.11,...

    苹果提供的常用的数据压缩算法LZMA、ZLIB、LZ4等;

    这三种算法也是苹果建议的,可跨平台使用;

    定义如下:

        /* Commonly-available encoders */
        COMPRESSION_LZ4     = 0x100,       // available starting OS X 10.11, iOS 9.0
        COMPRESSION_ZLIB    = 0x205,       // available starting OS X 10.11, iOS 9.0
        COMPRESSION_LZMA    = 0x306,       // available starting OS X 10.11, iOS 9.0
    
        COMPRESSION_LZ4_RAW = 0x101,       // available starting OS X 10.11, iOS 9.0
    
        /* Apple-specific encoders */
        COMPRESSION_LZFSE    = 0x801,      // available starting OS X 10.11, iOS 9.0
    

     

    适用于有大文件数据上传下载,节省流量可以考虑使用;

    使用说明:

    1. 需要引用头文件

    #include "compression.h"
    

    2. 数据压缩示例:

    - (void)testCompress{
        
        
        NSData *src = [NSData dataWithContentsOfFile:@"/Users/cocoajin/Desktop/src.txt"];
        
        NSLog(@"src: %ld",src.length);
        
        uint8_t *dstBuffer = (uint8_t *)malloc(src.length);
        memset(dstBuffer, 0, src.length);
        
        size_t compressResultLength = compression_encode_buffer(dstBuffer, src.length, [src bytes], src.length, NULL, COMPRESSION_LZMA);
        
        if (compressResultLength > 0) {
            NSData *newData = [NSData dataWithBytes:dstBuffer length:compressResultLength];
            [newData writeToFile:@"/Users/cocoajin/Desktop/compress.data" atomically:YES];
            NSLog(@"com: %ld",compressResultLength);
            NSLog(@"com: %.2f",(src.length-compressResultLength)/(float)src.length);
            
        }else{
            NSLog(@"compress error!");
        }
        
        free(dstBuffer);
    }
    

      

    3. 数据解压缩示例:

    - (void)testDeCompress{
        NSData *src = [NSData dataWithContentsOfFile:@"/Users/cocoajin/Desktop/compress.data"];
        
        NSLog(@"src: %ld",src.length);
        uint8_t *dstBuffer = (uint8_t *)malloc(1024*1024*10);
        memset(dstBuffer, 0, src.length);
        
        size_t decompressLen = compression_decode_buffer(dstBuffer,1024*1024*10,src.bytes,src.length,NULL,COMPRESSION_LZMA);
        
        if (decompressLen > 0) {
            NSData *newData = [NSData dataWithBytes:dstBuffer length:decompressLen];
            [newData writeToFile:@"/Users/cocoajin/Desktop/decompress.txt" atomically:YES];
            NSLog(@"com: %ld",decompressLen);
            
        }else{
            NSLog(@"decompressLen error!");
        }
        
        free(dstBuffer);
    }
    

     

    4. 实际测对一个1.9M的txt小说文件压缩,压缩以后大小为0.8m,压缩效果还是很明显的;

     

      

     

    转载于:https://www.cnblogs.com/cocoajin/p/10456128.html

    展开全文
  • iOS 数据压缩与解压

    2019-07-31 17:17:36
    在实际开发中,我们经常要对比较大的数据进行压缩后再上传服务器,下面是我在项目中压缩数据时所使用的压缩方法: 对数据进行压缩: zipAndUnzip *zipAnd = [[zipAndUnzip alloc] init]; NSStri...

    转自: http://blog.csdn.net/dkq972958298/article/details/53321804

     

    在实际开发中,我们经常要对比较大的数据进行压缩后再上传服务器,下面是我在项目中压缩数据时所使用的压缩方法:

    对数据进行压缩:

     zipAndUnzip *zipAnd = [[zipAndUnzip alloc] init];
        
        NSString *str = @"六六六六了还是德国大使馆和低俗化工及第三方换个撒点恢复健康的十分好的设计哈佛的号发的撒开了凤凰健康的十分健康的沙发";
        NSData *data = [str dataUsingEncoding:NSUTF8StringEncoding];
        NSData *dataDeflate = [zipAnd gzipDeflate:data];


    对数据进行解压:

    NSData *dataInflate =  [zipAnd gzipInflate:dataDeflate];
        NSString *aString = [[NSString alloc] initWithData:dataInflate encoding:NSUTF8StringEncoding];


    压缩的方法是:

        //压缩  
        - (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 = (uInt)[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 = (uInt)([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];  
        }  
    

     

    解压缩的方法是:

        //解压缩  
        - (NSData *)gzipInflate:(NSData*)data  
        {  
            if ([data length] == 0) return data;  
              
            unsigned long full_length = [data length];  
            unsigned long  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 = (uInt)[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 = (uInt)([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;  
        }  
    

     

    这个压缩实质上是二进制压缩的文件,可以对任何二进制文件进行压缩.

    转载于:https://www.cnblogs.com/xiaoc1314/p/6845553.html

    展开全文
  • iOS中进行数据压缩

    2016-11-24 16:28:25
    在实际开发中,我们经常要对比较大的数据进行压缩后再上传服务器,下面是我在项目中压缩数据时所使用的压缩方法: 对数据进行压缩:  zipAndUnzip *zipAnd = [[zipAndUnzip alloc] init];    NSString *...
  • 坑一:本地上传的jpg图片可能已经经过压缩,使用NSData ...方法取出Data数据时,compressionQuality如果取1.0,得到的数据会比原图更大,一般取0.7时的大小是原图大小。 坑二:压缩好的NSData数据经过+ (UIImage *)i...
  • 5.0 大文件的下载(1)实现思路边接收数据边写文件以解决内存越来越大的问题 (2)核心代码 //当接收到服务器响应的时候调用,该方法只会调用一次 -(void)connection:(NSURLConnection *)connection didReceive...
  • iOS-gzip压缩和解压缩

    2017-12-13 17:24:28
    iOS-gzip压缩和解压缩
  • iOS zlib 压缩解压

    2016-08-10 14:03:13
    最近需要压缩解压问题,查找了...1.在我所需要的项目中需要解压缩从服务器中请求到的NSData数据,所有创建了一个NSData类extension 实现对请求到的data的解压缩,此demo代码中有压缩和解压缩两种方法。 可以比较一下
  • UIImageJPEGRepresentation函数有两个参数:引用的图片和压缩系数. UIImagePNGRepresentation(UIImage * image)函数只有一个引用图片参数. UIImagePNGRepresentation(UIImage * image)要比UIImageJPEGPresentation...
  • 声明:ios开发系列文章由本人在网络搜集整理而来,以供自己工作、学习之用 使用gzip来处理压缩的响应数据 从0.9版本开始,ASIHTTPRequest会提示服务器它可以接收gzip压缩过的数据。 许多web服务器可以在...
  • iOS - 图片压缩

    2017-09-22 09:28:28
    如果不对图片进行压缩,图片太大,那么用户等待时间过长,严重影响用户的体验,而且造成用户流量浪费。 图片压缩的概念 1:压” 是指文件体积变小,但是像素数不变,长宽尺寸不变,那么质量可能下降。 2:缩” ...
  • 关于IOS压缩图片

    2016-12-21 14:44:45
    之前项目的图片压缩用的是UIImageJPEGRepresentation方法中的压缩因子进行压缩,但是图片失真会很严重,今天又花了点时间了解了下。 首先要说明一下UIImageJPEGRepresentation和UIImagePNGRepresentation,亲测后...
  • 昨天搞了一天的GZip压缩,试了三种方式(libz库,ZipArchive,ASIHttpRequest),一开始都不成功。 理论上三个应该都能用的,但我都不行。等我试到第三种方式的时候才知道,不是我的问题,而是后台的问题(Java端...
  • 使用gzip来处理压缩的响应数据 从0.9版本开始,ASIHTTPRequest会提示服务器它可以接收gzip压缩过的数据。许多web服务器可以在数据被发送之前压缩这些数据——这可以加快下载速度减少流量使用,但会让服务器的cpu...
  • 图片的压缩其实是俩概念, 1、是 “压” 文件体积变小,但是像素数不变,长宽尺寸不变,那么质量可能下降, 2、是 “缩” 文件的尺寸变小,也就是像素数减少。长宽尺寸变小,文件体积同样会减小。 这个 ...
  • 文章经过作者同意,转发到本博客:Flutter包体积之数据区域压缩分析与实践。 背景:在存量iOS应用中,接入Flutter框架,即混合开发模式,大概会给存量的iOS应用增加10M的包体积。本篇文章介绍Flutter包体积优化的一...
1 2 3 4 5 ... 20
收藏数 14,698
精华内容 5,879
关键字:

data数据压缩 ios