在开发 Android 应用时,图片的完整性校验是一个常见但容易被忽略的细节。无论是从网络下载图片、从本地存储加载,还是通过第三方接口上传图片,校验图片是否完整都能有效避免因数据损坏导致的应用崩溃或错误显示。
一、为什么需要校验图片完整性?
- 网络传输可能中断:在网络不稳定时,下载的图片可能不完整,无法正确解析。
- 存储设备故障:存储损坏可能导致文件部分丢失或被篡改。
- 文件校验有助于安全性:确保图片数据未被恶意篡改。
-
防止崩溃:加载不完整或损坏的图片可能导致
BitmapFactory
抛出异常。
二、常见的校验方法
1. 校验文件大小
通过服务器提供的文件大小信息,对比下载后的图片文件的大小是否一致。
fun isFileSizeValid(file: File, expectedSize: Long): Boolean {
return file.length() == expectedSize
}
优点:实现简单,适用于从可信来源获取图片的场景。
缺点:无法判断图片内容是否被篡改。
2. 校验文件的 MD5/SHA-256 哈希值
通过预先计算图片的哈希值,下载完成后重新计算并比对。
计算文件的哈希值
import java.io.File
import java.io.FileInputStream
import java.security.MessageDigest
fun getFileHash(file: File, algorithm: String = "SHA-256"): String {
val digest = MessageDigest.getInstance(algorithm)
FileInputStream(file).use { fis ->
val buffer = ByteArray(1024)
var bytesRead: Int
while (fis.read(buffer).also { bytesRead = it } != -1) {
digest.update(buffer, 0, bytesRead)
}
}
return digest.digest().joinToString("") { "%02x".format(it) }
}
// 校验哈希值
fun isFileHashValid(file: File, expectedHash: String, algorithm: String = "SHA-256"): Boolean {
return getFileHash(file, algorithm) == expectedHash
}
优点:高效且安全,适用于对文件完整性要求较高的场景。
缺点:需要服务器预先提供图片的哈希值。
3. 解析图片的基础信息
通过加载图片的基础信息(如宽高、格式等),判断文件是否可用。
import android.graphics.BitmapFactory
fun isImageValid(file: File): Boolean {
val options = BitmapFactory.Options().apply { inJustDecodeBounds = true }
BitmapFactory.decodeFile(file.absolutePath, options)
return options.outWidth > 0 && options.outHeight > 0
}
优点:适用于快速判断图片文件是否损坏。
缺点:不能发现细微的文件内容被篡改的问题。
4. CRC 校验
计算文件的 CRC 校验值,用于快速校验文件内容的完整性。
import java.io.File
import java.util.zip.CRC32
fun getFileCrc32(file: File): Long {
val crc32 = CRC32()
file.inputStream().use { fis ->
val buffer = ByteArray(1024)
var bytesRead: Int
while (fis.read(buffer).also { bytesRead = it } != -1) {
crc32.update(buffer, 0, bytesRead)
}
}
return crc32.value
}
// 校验 CRC 值
fun isCrcValid(file: File, expectedCrc: Long): Boolean {
return getFileCrc32(file) == expectedCrc
}
优点:计算速度较快,适合大文件校验。
缺点:CRC 校验比 MD5 或 SHA-256 更容易被攻击者伪造。
三、综合实现案例
以下是一个综合示例,下载图片后进行完整性校验,并处理校验失败的情况:
import okhttp3.OkHttpClient
import okhttp3.Request
import java.io.File
fun downloadAndValidateImage(
url: String,
targetFile: File,
expectedHash: String
): Boolean {
val client = OkHttpClient()
val request = Request.Builder().url(url).build()
val response = client.newCall(request).execute()
if (!response.isSuccessful) {
println("Download failed with code: ${response.code}")
return false
}
response.body?.byteStream()?.use { inputStream ->
targetFile.outputStream().use { outputStream ->
inputStream.copyTo(outputStream)
}
}
// 校验图片完整性
return if (isFileHashValid(targetFile, expectedHash)) {
println("Image validation successful")
true
} else {
println("Image validation failed")
false
}
}
四、应用场景与优化建议
- 场景一:下载与加载图片
- 在下载完成后进行大小或哈希值校验。
- 在加载图片时先解析基础信息,避免崩溃。
- 场景二:图片上传
- 上传前计算并记录图片的哈希值,确保上传过程未被篡改。
- 优化建议
- 在网络请求中使用带有自动校验功能的库(如 Glide、Picasso)。
- 使用协程或异步任务避免阻塞主线程。
五、总结
在 Android 应用中实现图片完整性校验是一项提升用户体验和系统稳定性的关键措施。从简单的文件大小校验到更复杂的哈希值和 CRC 校验,选择适合的方案可以平衡开发复杂度与应用需求。