首页 文章

如何在上传到Parse之前压缩缩小图像的大小为PFFile? (迅速)

提问于
浏览
59

我正试图在手机上直接拍照后将图像文件上传到Parse . 但它引发了一个例外:

由于未捕获的异常'NSInvalidArgumentException'终止应用程序,原因:'PFFile不能大于10485760字节'

这是我的代码:

在第一个视图控制器:

override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
    if (segue.identifier == "getImage")
    {
        var svc = segue.destinationViewController as! ClothesDetail
        svc.imagePassed = imageView.image
    }
}

在上传图像的视图控制器中:

let imageData = UIImagePNGRepresentation(imagePassed)
let imageFile = PFFile(name: "\(picName).png", data: imageData)

var userpic = PFObject(className:"UserPic")
userpic["picImage"] = imageFile`

但我仍然需要将该照片上传到Parse . 有没有办法减小图像的大小或分辨率?

9 回答

  • 30

    是的,您可以使用 UIImageJPEGRepresentation 而不是 UIImagePNGRepresentation 来缩小图像文件大小 . 您可以按如下方式创建扩展UIImage:

    Xcode 8.2 • Swift 3.0.2

    extension UIImage {
        enum JPEGQuality: CGFloat {
            case lowest  = 0
            case low     = 0.25
            case medium  = 0.5
            case high    = 0.75
            case highest = 1
        }
    
        /// Returns the data for the specified image in JPEG format.
        /// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory.
        /// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format.
        func jpeg(_ quality: JPEGQuality) -> Data? {
            return UIImageJPEGRepresentation(self, quality.rawValue)
        }
    }
    

    编辑/更新:

    Xcode 10 Swift 4.2

    extension UIImage {
        enum JPEGQuality: CGFloat {
            case lowest  = 0
            case low     = 0.25
            case medium  = 0.5
            case high    = 0.75
            case highest = 1
        }
    
        /// Returns the data for the specified image in JPEG format.
        /// If the image object’s underlying image data has been purged, calling this function forces that data to be reloaded into memory.
        /// - returns: A data object containing the JPEG data, or nil if there was a problem generating the data. This function may return nil if the image has no data or if the underlying CGImageRef contains data in an unsupported bitmap format.
        func jpeg(_ jpegQuality: JPEGQuality) -> Data? {
            return jpegData(compressionQuality: jpegQuality.rawValue)
        }
    }
    

    用法:

    if let imageData = image.jpeg(.lowest) {
        print(imageData.count)
    }
    
  • 146

    如果你想将图像的大小限制为一些具体的值,你可以这样做:

    import UIKit
    
    extension UIImage {
        // MARK: - UIImage+Resize
        func compressTo(_ expectedSizeInMb:Int) -> UIImage? {
            let sizeInBytes = expectedSizeInMb * 1024 * 1024
            var needCompress:Bool = true
            var imgData:Data?
            var compressingValue:CGFloat = 1.0
            while (needCompress && compressingValue > 0.0) {
            if let data:Data = UIImageJPEGRepresentation(self, compressingValue) {
                if data.count < sizeInBytes {
                    needCompress = false
                    imgData = data
                } else {
                    compressingValue -= 0.1
                }
            }
        }
    
        if let data = imgData {
            if (data.count < sizeInBytes) {
                return UIImage(data: data)
            }
        }
            return nil
        } 
    }
    
  • 1

    Jus Fixing for Xcode 7,于2015年9月21日测试并正常工作:

    只需创建一个扩展 UIImage ,如下所示:

    extension UIImage
    {
        var highestQualityJPEGNSData: NSData { return UIImageJPEGRepresentation(self, 1.0)! }
        var highQualityJPEGNSData: NSData    { return UIImageJPEGRepresentation(self, 0.75)!}
        var mediumQualityJPEGNSData: NSData  { return UIImageJPEGRepresentation(self, 0.5)! }
        var lowQualityJPEGNSData: NSData     { return UIImageJPEGRepresentation(self, 0.25)!}
        var lowestQualityJPEGNSData: NSData  { return UIImageJPEGRepresentation(self, 0.0)! }
    }
    

    然后你可以像这样使用它:

    let imageData = imagePassed.lowestQualityJPEGNSData
    
  • 0
    //image compression
    func resizeImage(image: UIImage) -> UIImage {
        var actualHeight: Float = Float(image.size.height)
        var actualWidth: Float = Float(image.size.width)
        let maxHeight: Float = 300.0
        let maxWidth: Float = 400.0
        var imgRatio: Float = actualWidth / actualHeight
        let maxRatio: Float = maxWidth / maxHeight
        let compressionQuality: Float = 0.5
        //50 percent compression
    
        if actualHeight > maxHeight || actualWidth > maxWidth {
            if imgRatio < maxRatio {
                //adjust width according to maxHeight
                imgRatio = maxHeight / actualHeight
                actualWidth = imgRatio * actualWidth
                actualHeight = maxHeight
            }
            else if imgRatio > maxRatio {
                //adjust height according to maxWidth
                imgRatio = maxWidth / actualWidth
                actualHeight = imgRatio * actualHeight
                actualWidth = maxWidth
            }
            else {
                actualHeight = maxHeight
                actualWidth = maxWidth
            }
        }
    
        let rect = CGRectMake(0.0, 0.0, CGFloat(actualWidth), CGFloat(actualHeight))
        UIGraphicsBeginImageContext(rect.size)
        image.drawInRect(rect)
        let img = UIGraphicsGetImageFromCurrentImageContext()
        let imageData = UIImageJPEGRepresentation(img!,CGFloat(compressionQuality))
        UIGraphicsEndImageContext()
        return UIImage(data: imageData!)!
    }
    
  • 8

    这是非常简单的 UIImage 扩展名

    extension UIImage {
    
    func resizeByByte(maxByte: Int, completion: @escaping (Data) -> Void) {
        var compressQuality: CGFloat = 1
        var imageData = Data()
        var imageByte = UIImageJPEGRepresentation(self, 1)?.count
    
        while imageByte! > maxByte {
            imageData = UIImageJPEGRepresentation(self, compressQuality)!
            imageByte = UIImageJPEGRepresentation(self, compressQuality)?.count
            compressQuality -= 0.1
        }
    
        if maxByte > imageByte! {
            completion(imageData)
        } else {
            completion(UIImageJPEGRepresentation(self, 1)!)
        }
    }
    

    使用

    // max 300kb
    image.resizeByByte(maxByte: 300000) { (resizedData) in
        print("image size: \(resizedData.count)")
    }
    
  • 5

    在Swift 4中,我创建了这个扩展,它将获得预期的大小以尝试到达它 .

    extension UIImage {
    
        enum CompressImageErrors: Error {
            case invalidExSize
            case sizeImpossibleToReach
        }
        func compressImage(_ expectedSizeKb: Int, completion : (UIImage,CGFloat) -> Void ) throws {
    
            let minimalCompressRate :CGFloat = 0.4 // min compressRate to be checked later
    
            if expectedSizeKb == 0 {
                throw CompressImageErrors.invalidExSize // if the size is equal to zero throws
            }
    
            let expectedSizeBytes = expectedSizeKb * 1024
            let imageToBeHandled: UIImage = self
            var actualHeight : CGFloat = self.size.height
            var actualWidth : CGFloat = self.size.width
            var maxHeight : CGFloat = 841 //A4 default size I'm thinking about a document
            var maxWidth : CGFloat = 594
            var imgRatio : CGFloat = actualWidth/actualHeight
            let maxRatio : CGFloat = maxWidth/maxHeight
            var compressionQuality : CGFloat = 1
            var imageData:Data = UIImageJPEGRepresentation(imageToBeHandled, compressionQuality)!
            while imageData.count > expectedSizeBytes {
    
                if (actualHeight > maxHeight || actualWidth > maxWidth){
                    if(imgRatio < maxRatio){
                        imgRatio = maxHeight / actualHeight;
                        actualWidth = imgRatio * actualWidth;
                        actualHeight = maxHeight;
                    }
                    else if(imgRatio > maxRatio){
                        imgRatio = maxWidth / actualWidth;
                        actualHeight = imgRatio * actualHeight;
                        actualWidth = maxWidth;
                    }
                    else{
                        actualHeight = maxHeight;
                        actualWidth = maxWidth;
                        compressionQuality = 1;
                    }
                }
                let rect = CGRect(x: 0.0, y: 0.0, width: actualWidth, height: actualHeight)
                UIGraphicsBeginImageContext(rect.size);
                imageToBeHandled.draw(in: rect)
                let img = UIGraphicsGetImageFromCurrentImageContext();
                UIGraphicsEndImageContext();
                    if let imgData = UIImageJPEGRepresentation(img!, compressionQuality) {
                    if imgData.count > expectedSizeBytes {
                        if compressionQuality > minimalCompressRate {
                            compressionQuality -= 0.1
                        } else {
                            maxHeight = maxHeight * 0.9
                            maxWidth = maxWidth * 0.9
                        }
                    }
                    imageData = imgData
                }
    
    
            }
    
            completion(UIImage(data: imageData)!, compressionQuality)
        }
    
    
    }
    

    使用

    do {
                try UiImageView.image?.compressImage(100, completion: { (image, compressRatio) in
                    print(image.size) 
                    imageData = UIImageJPEGRepresentation(image, compressRatio)
                    base64data = imageData?.base64EncodedString()
    
                })
            } catch {
                     print("Error")
            }
    
  • 1

    Swift 4 Binary Approach 压缩图像

    我认为回答这个问题已经很晚了但是这里是我对优化问题的解决方案我正在使用_1139838来找到最佳值 . 因此,例如,通过正常减法方法达到62%将需要38次压缩尝试,*二进制搜索**方法将达到最大日志(100)=大约7次尝试所需的解决方案 .

    但是,还要通知您 UIImageJPEGRepresentation 函数的行为不是线性的,特别是当数字达到接近1.这是屏幕抓取,我们可以看到图像在浮点值> 0.995后停止压缩 . 这种行为是非常不可预测的,所以最好有一个delta缓冲区来处理这种情况 .

    enter image description here

    这是它的代码

    extension UIImage {
        func resizeToApprox(sizeInMB: Double, deltaInMB: Double = 0.2) -> Data {
            let allowedSizeInBytes = Int(sizeInMB * 1024 * 1024)
            let deltaInBytes = Int(deltaInMB * 1024 * 1024)
            let fullResImage = UIImageJPEGRepresentation(self, 1.0)
            if (fullResImage?.count)! < Int(deltaInBytes + allowedSizeInBytes) {
                return fullResImage!
            }
    
            var i = 0
    
            var left:CGFloat = 0.0, right: CGFloat = 1.0
            var mid = (left + right) / 2.0
            var newResImage = UIImageJPEGRepresentation(self, mid)
    
            while (true) {
                i += 1
                if (i > 13) {
                    print("Compression ran too many times ") // ideally max should be 7 times as  log(base 2) 100 = 6.6
                    break
                }
    
    
                print("mid = \(mid)")
    
                if ((newResImage?.count)! < (allowedSizeInBytes - deltaInBytes)) {
                    left = mid
                } else if ((newResImage?.count)! > (allowedSizeInBytes + deltaInBytes)) {
                    right = mid
                } else {
                    print("loop ran \(i) times")
                    return newResImage!
                }
                 mid = (left + right) / 2.0
                newResImage = UIImageJPEGRepresentation(self, mid)
    
            }
    
            return UIImageJPEGRepresentation(self, 0.5)!
        }
    }
    
  • 0

    Swift 3

    @ leo-dabus回答修订为swift 3

    extension UIImage {
        var uncompressedPNGData: Data?      { return UIImagePNGRepresentation(self)        }
        var highestQualityJPEGNSData: Data? { return UIImageJPEGRepresentation(self, 1.0)  }
        var highQualityJPEGNSData: Data?    { return UIImageJPEGRepresentation(self, 0.75) }
        var mediumQualityJPEGNSData: Data?  { return UIImageJPEGRepresentation(self, 0.5)  }
        var lowQualityJPEGNSData: Data?     { return UIImageJPEGRepresentation(self, 0.25) }
        var lowestQualityJPEGNSData:Data?   { return UIImageJPEGRepresentation(self, 0.0)  }
    }
    
  • 2

    In Swift

    func ResizeImageOriginalSize(targetSize: CGSize) -> UIImage {
            var actualHeight: Float = Float(self.size.height)
            var actualWidth: Float = Float(self.size.width)
            let maxHeight: Float = Float(targetSize.height)
            let maxWidth: Float = Float(targetSize.width)
            var imgRatio: Float = actualWidth / actualHeight
            let maxRatio: Float = maxWidth / maxHeight
            var compressionQuality: Float = 0.5
            //50 percent compression
    
            if actualHeight > maxHeight || actualWidth > maxWidth {
                if imgRatio < maxRatio {
                    //adjust width according to maxHeight
                    imgRatio = maxHeight / actualHeight
                    actualWidth = imgRatio * actualWidth
                    actualHeight = maxHeight
                }
                else if imgRatio > maxRatio {
                    //adjust height according to maxWidth
                    imgRatio = maxWidth / actualWidth
                    actualHeight = imgRatio * actualHeight
                    actualWidth = maxWidth
                }
                else {
                    actualHeight = maxHeight
                    actualWidth = maxWidth
                    compressionQuality = 1.0
                }
            }
            let rect = CGRect(x: 0.0, y: 0.0, width: CGFloat(actualWidth), height: CGFloat(actualHeight))
            UIGraphicsBeginImageContextWithOptions(rect.size, false, CGFloat(compressionQuality))
            self.draw(in: rect)
            let newImage = UIGraphicsGetImageFromCurrentImageContext()
            UIGraphicsEndImageContext()
            return newImage!
        }
    

相关问题