Retrofit Multipart上传图片使用image/*返回400错误原因及解决方案
问题产生原因
- HTTP规范中,
image/*这类带通配符*的MIME写法,仅允许出现在Accept请求头中,用于告知服务端客户端可接收的响应类型范围,不能作为实体内容的Content-Type值使用。 - Retrofit底层依赖OkHttp构造Multipart请求时,不会主动解析替换通配符MIME,会直接把你传入的
image/*写到图片分片的Content-Type头中透传给后端。绝大多数后端的文件上传校验逻辑(包括常用的Spring、Express文件上传中间件)只会做精确MIME匹配,不会识别通配符,收到非法的Content-Type值会直接判定请求参数不合法,返回400 Bad Request。 - 手动指定
image/jpg这类明确MIME后,分片头的Content-Type符合后端校验规则,自然就能上传成功。
支持任意图片格式上传的正确实现
不要硬编码固定MIME,也不要使用通配符,应该在构造请求前自动探测图片文件的真实MIME类型,Android端可以直接用系统自带的MimeTypeMap实现,无需引入额外依赖:
- 编写MIME探测工具方法,兜底处理无法识别类型的场景:
import android.webkit.MimeTypeMap import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.RequestBody.Companion.asRequestBody import java.io.File private fun fetchFileMimeType(file: File): String { val fileExtension = MimeTypeMap.getFileExtensionFromUrl(file.name) ?: return "application/octet-stream" return MimeTypeMap.getSingleton() .getMimeTypeFromExtension(fileExtension.lowercase()) ?: "application/octet-stream" }
- 构造图片上传分片时,使用探测到的真实MIME,可提前增加类型校验拦截非图片文件:
// 获取文件真实MIME val mimeType = fetchFileMimeType(imageFile) // 校验是否为图片类型 check(mimeType.startsWith("image/")) { "所选文件不是合法图片格式" } // 构造请求体 val imageBody = imageFile.asRequestBody(mimeType.toMediaTypeOrNull()) val imagePart = MultipartBody.Part.createFormData("image", imageFile.name, imageBody)
补充说明:如果需要做更严格的安全校验,避免用户通过修改文件后缀上传恶意文件,可以在MIME探测逻辑中增加文件头魔数校验,读取文件前几个字节判断真实文件类型,校验通过后再构造上传请求。
内容的提问来源于stack exchange,提问作者Zen Ryuu
相关产品推荐
相关产品推荐

