swift 如何压缩图像从服务器到500kB?

egmofgnx  于 2023-03-11  发布在  Swift
关注(0)|答案(1)|浏览(243)

我想用UIActivityViewController通过KakaoTalk应用发送图片,但是KakaoTalk应用有每张图片500kB的容量限制,目前我从服务器接收到的照片每张图片都在8MB以上,不得不降低容量,但是好几天都没有解决。
虽然图像大小已被调整大小和压缩,但无论压缩多少,都很难将容量减少到500 kB或更少。我想知道另一种方法。
下面是调整图像大小的UIImage的扩展。

func resized(to length: CGFloat) -> UIImage {
        let width = self.size.width
        let height = self.size.height
        let resizeLength: CGFloat = length
        var scale: CGFloat

        if height >= width {
            scale = width <= resizeLength ? 1 : resizeLength / width
        } else {
            scale = height <= resizeLength ? 1 :resizeLength / height
        }
  
        let newHeight = height * scale
        let newWidth = width * scale
        let size = CGSize(width: newWidth, height: newHeight)
        let render = UIGraphicsImageRenderer(size: size)
        let renderImage = render.image { _ in
            self.draw(in: CGRect(origin: .zero, size: size))
        }
        return renderImage
    }

我用这个方法从服务器获取图像。

private func getImageFromURL(imageURL: String, completion: @escaping (UIImage) -> Void) {
        guard let url = URL(string: imageURL) else { return }

        DispatchQueue.global().async { [weak self] in
            if let data = try? Data(contentsOf: url) {
                print("⭐️data from server: \(data.count) bytes")

                if let image = UIImage(data: data) {
                    completion(image)
                }
            }
        }
    }

我通过UIActivityViewController共享图像

@objc func shareButtonTapped(_ sender: UIButton) {
        memoryViewModel.selectedDataArray = memoryViewModel.selectedImageDictionary.values.compactMap { image in

            // 전송 가능한 사이즈 500KB
            let maxSizeInBytes = 500 * 1024

            // 최초 압축률
            var compressionQuality: CGFloat = 1.0

            // 용량을 줄이기 위한 이미지 resize
            let resizedImage = image.resized(to: 1080)

            // 이미지 -> jpeg (최초 압축률 1.0 적용)
            var imageData = resizedImage.jpegData(compressionQuality: compressionQuality)
            print("🟥 before compression: \(imageData)")

            while imageData?.count ?? 0 > maxSizeInBytes && compressionQuality > 0 {
                compressionQuality -= 0.1
                imageData = image.jpegData(compressionQuality: compressionQuality)
            }
            
            print("🟩 after compression: \(imageData))")
            return image
        }

        let activityViewController = UIActivityViewController(activityItems: memoryViewModel.selectedDataArray, applicationActivities: nil)

        activityViewController.excludedActivityTypes = [UIActivity.ActivityType.addToReadingList]

        present(activityViewController, animated: true, completion: nil)
        
        activityViewController.completionWithItemsHandler = { (activity, completed, items, error) in

            if completed {
                //
            } else {
                //
            }
        }

    }

通过打印声明获得以下结果。

⭐️data from server: 8338918 bytes
🟥 before compression: Optional(11474975 bytes)
🟩 after compression: Optional(633815 bytes))

如何将从服务器获取的图片更改为所需的大小?

fnvucqvd

fnvucqvd1#

主要问题是resized方法。您使用的:

let render = UIGraphicsImageRenderer(size: size)

依赖于一个默认格式,该默认格式为图像提供了与当前设备相同的缩放比例。您生成的大小以点为单位。因此,实际图像(以像素为单位)是点大小乘以图像缩放比例。对于大多数设备,这将使图像的大小增大4倍或9倍。
请注意,您的图像从8MB开始,然后在您将其缩放(假设为更小的大小)后,图像会膨胀到11MB。您试图生成越来越低质量的JPEG,但无法使图像数据大小足够小。
若要解决此问题,请使用比例1.0设置图像渲染,而不是依赖默认比例。
按如下所示更新代码:

var fmt = UIGraphicsImageRendererFormat.default()
fmt.scale = 1.0
let render = UIGraphicsImageRenderer(size: size, format: fmt)

这将导致重新调整大小的图像在点和像素上都是相同的大小,这将给予一个小得多的初始JPEG。您的逻辑的其余部分现在应该导致大小在您达到压缩因子0.1之前低于您所需的最大值500kB。
下面是完整的resized方法,只是做了一些小改动:

func resized(to length: CGFloat) -> UIImage {
    let width = self.size.width
    let height = self.size.height
    let resizeLength: CGFloat = length
    var scale: CGFloat

    if height >= width {
        scale = width <= resizeLength ? 1 : resizeLength / width
    } else {
        scale = height <= resizeLength ? 1 :resizeLength / height
    }

    let newHeight = height * scale
    let newWidth = width * scale
    let size = CGSize(width: newWidth, height: newHeight)
    // Setup the format with a scale of 1.0
    var fmt = UIGraphicsImageRendererFormat.default()
    fmt.scale = 1.0
    let render = UIGraphicsImageRenderer(size: size, format: fmt)
    let renderImage = render.image { _ in
        self.draw(in: CGRect(origin: .zero, size: size))
    }
    return renderImage
}

相关问题