Swift4(Xcode9.2)iOS应用图片Base64编解码后显示异常求助
嘿,我看你在Swift 4(Xcode 9.2)开发iOS应用时,遇到了图片转Base64存入数据库后,解码显示黑图还报read_user_chunkIDOT:1221: invalid PNG file: no valid iEnd chunk的错误——这大概率是编码过程中图片数据生成不完整或者格式处理出了问题,咱们一步步来排查解决:
可能的原因及对应解决方案
1. UIImage转Data时格式处理异常
你大概率是用了UIImagePNGRepresentation来生成PNG数据,但这个方法在处理某些从相册获取的图片(比如带EXIF信息的压缩图、非PNG源图)时,容易生成不完整的PNG数据,导致解码时找不到关键的iEnd chunk。
解决办法:
- 如果不需要透明通道,优先用JPEG格式转Data,压缩质量选0.7-0.8兼顾画质和大小:
func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let selectedImage = info[.originalImage] as? UIImage else { picker.dismiss(animated: true) return } // 转JPEG Data if let imageData = UIImageJPEGRepresentation(selectedImage, 0.8) { let base64String = imageData.base64EncodedString(options: []) // 存入数据库 } picker.dismiss(animated: true) } - 如果必须用PNG格式,先对图片重绘生成完整的PNG数据:
func convertToValidPNGData(_ image: UIImage) -> Data? { UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) defer { UIGraphicsEndImageContext() } image.draw(in: CGRect(origin: .zero, size: image.size)) return UIGraphicsGetImageFromCurrentImageContext()?.pngData() } // 在图片选择回调里使用 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let selectedImage = info[.originalImage] as? UIImage else { picker.dismiss(animated: true) return } if let validPNGData = convertToValidPNGData(selectedImage) { let base64String = validPNGData.base64EncodedString(options: []) // 存入数据库 } picker.dismiss(animated: true) }
2. Base64编码/解码选项不匹配
如果编码时使用了.lineLength64Characters这类添加换行的选项,解码时若没有对应处理,会导致数据截断或损坏。
解决办法:
编码和解码都使用无额外选项的方式,避免格式干扰:
// 编码 let base64String = imageData.base64EncodedString(options: []) // 解码 if let decodedData = Data(base64Encoded: base64String), let decodedImage = UIImage(data: decodedData) { // 展示图片 yourImageView.image = decodedImage }
3. 数据库存储字段长度不足
如果你的数据库字段(比如MySQL的VARCHAR)长度不够,Base64字符串会被截断,导致解码时文件损坏。
解决办法:
把存储Base64的字段改成足够大的类型,比如MySQL用TEXT或LONGTEXT,Core Data里用Binary Data类型(其实直接存Data比Base64更高效,你也可以考虑直接存图片Data,省去Base64转换步骤)。
4. 相册图片的原始数据异常
从相册获取的图片可能带有额外的元数据或被系统压缩,直接转换容易出问题。
解决办法:
先对图片进行尺寸压缩,再转换编码:
func resizeImage(_ image: UIImage, targetMaxSize: CGFloat) -> UIImage { let originalSize = image.size var targetSize = originalSize if originalSize.width > targetMaxSize || originalSize.height > targetMaxSize { let ratio = min(targetMaxSize/originalSize.width, targetMaxSize/originalSize.height) targetSize = CGSize(width: originalSize.width*ratio, height: originalSize.height*ratio) } UIGraphicsBeginImageContextWithOptions(targetSize, false, image.scale) image.draw(in: CGRect(origin: .zero, size: targetSize)) let resizedImage = UIGraphicsGetImageFromCurrentImageContext()! UIGraphicsEndImageContext() return resizedImage } // 在图片选择回调里使用 func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) { guard let selectedImage = info[.originalImage] as? UIImage else { picker.dismiss(animated: true) return } let resizedImage = resizeImage(selectedImage, targetMaxSize: 1000) // 再转Data和Base64 if let imageData = UIImageJPEGRepresentation(resizedImage, 0.8) { let base64String = imageData.base64EncodedString(options: []) // 存入数据库 } picker.dismiss(animated: true) }
额外排查技巧
你可以在编码后把生成的Data保存到本地,看看能不能正常打开:
// 测试用:把Data保存到Documents目录 let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true) let documentsDirectory = paths[0] let filePath = "\(documentsDirectory)/test.png" try? imageData.write(to: URL(fileURLWithPath: filePath))
如果本地文件能打开,说明编码没问题,问题出在数据库存储或解码环节;如果本地文件也打不开,那就是编码环节的问题,优先调整转Data的方式。
内容的提问来源于stack exchange,提问作者Mighri Radhia

