Fix the upload of heavy avatar or header image (#2103)

* Compressor: allows custom parameters for compressing image (maxSize, maxHeight, maxWidth)

* Account: configures the maximum image size for uploading avatar and header
This commit is contained in:
Christopher Schindler 2024-06-24 08:58:06 +02:00 committed by GitHub
parent 02a8cb12e9
commit aaeb9eaa36
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 66 additions and 17 deletions

View file

@ -51,17 +51,19 @@ import SwiftUI
didSet {
if let item = mediaPickers.first {
Task {
if let data = await getItemImageData(item: item) {
if isChangingAvatar {
_ = await uploadAvatar(data: data)
if let data = await getItemImageData(item: item, for: .avatar) {
_ = await uploadAvatar(data: data)
}
isChangingAvatar = false
} else if isChangingHeader {
_ = await uploadHeader(data: data)
if let data = await getItemImageData(item: item, for: .header) {
_ = await uploadHeader(data: data)
}
isChangingHeader = false
}
await fetchAccount()
isChangingAvatar = false
isChangingHeader = false
mediaPickers = []
}
}
}
}
@ -140,16 +142,48 @@ import SwiftUI
}
}
private func getItemImageData(item: PhotosPickerItem) async -> Data? {
private func getItemImageData(item: PhotosPickerItem, for type: ItemType) async -> Data? {
guard let imageFile = try? await item.loadTransferable(type: StatusEditor.ImageFileTranseferable.self) else { return nil }
let compressor = StatusEditor.Compressor()
guard let compressedData = await compressor.compressImageFrom(url: imageFile.url),
let image = UIImage(data: compressedData),
let uploadData = try? await compressor.compressImageForUpload(image)
else { return nil }
let uploadData = try? await compressor.compressImageForUpload(
image,
maxSize: 2 * 1024 * 1024, // 2MB
maxHeight: type.maxHeight,
maxWidth: type.maxWidth
)
else {
return nil
}
return uploadData
}
}
extension EditAccountViewModel {
private enum ItemType {
case avatar
case header
var maxHeight: CGFloat {
switch self {
case .avatar:
400
case .header:
500
}
}
var maxWidth: CGFloat {
switch self {
case .avatar:
400
case .header:
1500
}
}
}
}

View file

@ -57,30 +57,45 @@ public extension StatusEditor {
}
}
public func compressImageForUpload(_ image: UIImage) async throws -> Data {
public func compressImageForUpload(
_ image: UIImage,
maxSize: Int = 10 * 1024 * 1024,
maxHeight: Double = 5000,
maxWidth: Double = 5000
) async throws -> Data {
var image = image
if image.size.height > 5000 || image.size.width > 5000 {
image = image.resized(to: .init(width: image.size.width / 4,
height: image.size.height / 4))
if image.size.height > maxHeight || image.size.width > maxWidth {
let heightFactor = image.size.height / maxHeight
let widthFactor = image.size.width / maxWidth
let maxFactor = max(heightFactor, widthFactor)
image = image.resized(to: .init(width: image.size.width / maxFactor,
height: image.size.height / maxFactor))
}
guard var imageData = image.jpegData(compressionQuality: 0.8) else {
throw CompressorError.noData
}
let maxSize = 10 * 1024 * 1024
var compressionQualityFactor: CGFloat = 0.8
if imageData.count > maxSize {
while imageData.count > maxSize {
while imageData.count > maxSize && compressionQualityFactor >= 0 {
guard let compressedImage = UIImage(data: imageData),
let compressedData = compressedImage.jpegData(compressionQuality: 0.8)
let compressedData = compressedImage.jpegData(compressionQuality: compressionQualityFactor)
else {
throw CompressorError.noData
}
imageData = compressedData
compressionQualityFactor -= 0.1
}
}
if imageData.count > maxSize && compressionQualityFactor <= 0 {
throw CompressorError.noData
}
return imageData
}