改造 android 10 kotlin 的图片上传问题
Image Upload issue with retrofit for android 10 kotlin
Api 界面:
@Multipart
@POST(AppConstants.UPDATE_PRODUCT)
fun updateProduct(
@Header("Authorization") auth: String,
@Part("product_id") id: RequestBody,
@Part("title") title: RequestBody,
@Part("description") description: RequestBody,
@Part("price") price: RequestBody,
@Part image: MultipartBody.Part
) : Call<UpdateProductResponse>?
意图:
val photoIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "image/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
startActivityForResult(photoIntent, UPLOAD_IMAGE)
Activity 结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPLOAD_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, data.data)
update_iv.setImageBitmap(bitmap)
val imageType = contentResolver.getType(data.data!!)
val extension = imageType!!.substring(imageType.indexOf("/") + 1)
val file = File(getPath(data.data!!, this)!!)
//val byte = contentResolver.openInputStream(data.data!!)
//val contentPart = InputStreamRequestBody("image/*".toMediaType() , contentResolver , data.data!!)
//filePartImage = MultipartBody.Part.createFormData("image" , file.name , contentPart)
data.data!!.let {
application.contentResolver.openInputStream(it)?.use { inputStream ->
filePartImage = MultipartBody.Part.createFormData(
"image",
"" + file.name, //or "image.$extension" none of the two works
inputStream.readBytes().toRequestBody("image/*".toMediaType())
)
}
}
showMessage(this, "" + getString(R.string.image_uploaded))
} else {
showMessage(this, "" + "File upload failed!")
}
}
}
Api 调用:
这里的 filePartImage 是这样的全局变量:private var filePartImage: MultipartBody.Part? =空
RetrofitClientInstance().createService(ArtOFiestaServiceClass::class.java).updateProduct(
"Bearer " + Prefs.getString("token", ""),
id.trim().toRequestBody("text/plain".toMediaType()),
update_name.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_desc.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_price.text.toString().trim().toRequestBody("text/plain".toMediaType()),
filePartImage!!
)?.enqueue(object : retrofit2.Callback<UpdateProductResponse> {
override fun onResponse(
call: Call<UpdateProductResponse>,
response: Response<UpdateProductResponse>
)
清单:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
android:requestLegacyExternalStorage="true"
我在node js中开发了它的后端并使用multer上传文件。我可以使用邮递员上传文件,但无法通过我的移动应用程序上传。我的代码有什么问题?可能是 android 分区存储问题?
问题出在Scoped Storage
,因为我也遇到了同样的问题
我的解决方案是将 inputStream
转换为相同的 File
并将其存储在其他地方。然后上传后,您可以删除文件。
思路如下
contentResolver.openInputStream(it)?.use { inputStream ->
// STEP 1: Create a tempFile for storing the image from scoped storage.
val tempFile = createTempFile(this, fileName, extension)
// STEP 2: copy inputStream into the tempFile
copyStreamToFile(it, tempFile)
// STEP 3: get file path from tempFile for further upload process.
val filePath = tempFile.absolutePath
val requestFile: RequestBody = File(filePath).asRequestBody(fileType?.toMediaTypeOrNull())
val body: MultipartBody.Part = MultipartBody.Part.createFormData("file", fileName, requestFile)
}
@Throws(IOException::class)
fun createTempFile(context: Context, fileName: String?, extension: String?): File {
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
return File(storageDir, "$fileName.$extension")
}
fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
将 .toRequestBody("image/*".toMediaType())
替换为 .toRequestBody(imageType.toMediaTypeOrNull)
完成了工作
if (requestCode == UPLOAD_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, data.data)
update_iv.setImageBitmap(bitmap)
val imageType = contentResolver.getType(data.data!!)
val extension = imageType!!.substring(imageType.indexOf("/") + 1)
val file = File(getPath(data.data!!, this)!!)
data.data!!.let {
application.contentResolver.openInputStream(it).use { inputStream ->
filePartImage = MultipartBody.Part.createFormData(
"image",
file.name,
inputStream!!.readBytes().toRequestBody(imageType.toMediaTypeOrNull())
)
}
}
Api 界面:
@Multipart
@POST(AppConstants.UPDATE_PRODUCT)
fun updateProduct(
@Header("Authorization") auth: String,
@Part("product_id") id: RequestBody,
@Part("title") title: RequestBody,
@Part("description") description: RequestBody,
@Part("price") price: RequestBody,
@Part image: MultipartBody.Part
) : Call<UpdateProductResponse>?
意图:
val photoIntent = Intent(Intent.ACTION_GET_CONTENT).apply {
type = "image/*"
addCategory(Intent.CATEGORY_OPENABLE)
}
startActivityForResult(photoIntent, UPLOAD_IMAGE)
Activity 结果:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPLOAD_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, data.data)
update_iv.setImageBitmap(bitmap)
val imageType = contentResolver.getType(data.data!!)
val extension = imageType!!.substring(imageType.indexOf("/") + 1)
val file = File(getPath(data.data!!, this)!!)
//val byte = contentResolver.openInputStream(data.data!!)
//val contentPart = InputStreamRequestBody("image/*".toMediaType() , contentResolver , data.data!!)
//filePartImage = MultipartBody.Part.createFormData("image" , file.name , contentPart)
data.data!!.let {
application.contentResolver.openInputStream(it)?.use { inputStream ->
filePartImage = MultipartBody.Part.createFormData(
"image",
"" + file.name, //or "image.$extension" none of the two works
inputStream.readBytes().toRequestBody("image/*".toMediaType())
)
}
}
showMessage(this, "" + getString(R.string.image_uploaded))
} else {
showMessage(this, "" + "File upload failed!")
}
}
}
Api 调用: 这里的 filePartImage 是这样的全局变量:private var filePartImage: MultipartBody.Part? =空
RetrofitClientInstance().createService(ArtOFiestaServiceClass::class.java).updateProduct(
"Bearer " + Prefs.getString("token", ""),
id.trim().toRequestBody("text/plain".toMediaType()),
update_name.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_desc.text.toString().trim().toRequestBody("text/plain".toMediaType()),
update_price.text.toString().trim().toRequestBody("text/plain".toMediaType()),
filePartImage!!
)?.enqueue(object : retrofit2.Callback<UpdateProductResponse> {
override fun onResponse(
call: Call<UpdateProductResponse>,
response: Response<UpdateProductResponse>
)
清单:
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_MEDIA_LOCATION"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
android:requestLegacyExternalStorage="true"
我在node js中开发了它的后端并使用multer上传文件。我可以使用邮递员上传文件,但无法通过我的移动应用程序上传。我的代码有什么问题?可能是 android 分区存储问题?
问题出在Scoped Storage
,因为我也遇到了同样的问题
我的解决方案是将 inputStream
转换为相同的 File
并将其存储在其他地方。然后上传后,您可以删除文件。
思路如下
contentResolver.openInputStream(it)?.use { inputStream ->
// STEP 1: Create a tempFile for storing the image from scoped storage.
val tempFile = createTempFile(this, fileName, extension)
// STEP 2: copy inputStream into the tempFile
copyStreamToFile(it, tempFile)
// STEP 3: get file path from tempFile for further upload process.
val filePath = tempFile.absolutePath
val requestFile: RequestBody = File(filePath).asRequestBody(fileType?.toMediaTypeOrNull())
val body: MultipartBody.Part = MultipartBody.Part.createFormData("file", fileName, requestFile)
}
@Throws(IOException::class)
fun createTempFile(context: Context, fileName: String?, extension: String?): File {
val storageDir: File? = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS)
return File(storageDir, "$fileName.$extension")
}
fun copyStreamToFile(inputStream: InputStream, outputFile: File) {
inputStream.use { input ->
val outputStream = FileOutputStream(outputFile)
outputStream.use { output ->
val buffer = ByteArray(4 * 1024) // buffer size
while (true) {
val byteCount = input.read(buffer)
if (byteCount < 0) break
output.write(buffer, 0, byteCount)
}
output.flush()
}
}
}
将 .toRequestBody("image/*".toMediaType())
替换为 .toRequestBody(imageType.toMediaTypeOrNull)
完成了工作
if (requestCode == UPLOAD_IMAGE && resultCode == RESULT_OK) {
if (data != null) {
val bitmap = MediaStore.Images.Media.getBitmap(this.contentResolver, data.data)
update_iv.setImageBitmap(bitmap)
val imageType = contentResolver.getType(data.data!!)
val extension = imageType!!.substring(imageType.indexOf("/") + 1)
val file = File(getPath(data.data!!, this)!!)
data.data!!.let {
application.contentResolver.openInputStream(it).use { inputStream ->
filePartImage = MultipartBody.Part.createFormData(
"image",
file.name,
inputStream!!.readBytes().toRequestBody(imageType.toMediaTypeOrNull())
)
}
}