如何在Android Studio中使用VolleyMultipart在单次请求中上传多个文件
实现单次请求上传多个文件的改造方案
1. 改造上传函数,支持多文件批量传入
将原本接收单个文件的上传函数调整为接收文件名+Uri的集合,批量读取所有文件字节后塞入 multipart 请求参数:
// 入参修改为 文件名-URI 集合 private fun uploadFiles(fileList: List<Pair<String, Uri>>) { var rQueue = Volley.newRequestQueue(this) val myurl = "https://apiurl" try { val filePartMap: MutableMap<String, DataPart> = HashMap() // 批量读取所有文件字节,存入参数Map fileList.forEachIndexed { index, (fileName, uri) -> val iStream = contentResolver.openInputStream(uri) val inputData = getBytes(iStream) // 注意:参数名需要和你服务端约定的多文件接收字段匹配 // 如果服务端接收数组格式,参数名用 evidence_image[$index] 或者 evidence_image[] filePartMap["evidence_image_$index"] = DataPart(fileName, inputData) } val volleyMultipartRequest: VolleyMultipartRequest = object : VolleyMultipartRequest( Method.POST, myurl, Response.Listener { response -> try { val jsonobject = JSONObject(String(response.data)) val returnmsg = jsonobject.getString("hua ki nhi") if(returnmsg == "aish kro ho gya") { Toast.makeText(this, "所有文件上传成功", Toast.LENGTH_SHORT).show() } else{ Toast.makeText(this, "文件上传失败", Toast.LENGTH_SHORT).show() } dialog.cancel() }catch (e: JSONException){ e.printStackTrace() dialog.cancel() } }, Response.ErrorListener { error -> Toast.makeText( applicationContext, error.message, Toast.LENGTH_SHORT ).show() dialog.cancel() }) { @Throws(AuthFailureError::class) override fun getParams(): Map<String, String>? { // 按需添加其他普通参数 return HashMap() } override fun getByteData(): Map<String, DataPart> { return filePartMap } } // 多文件上传体积大,建议调整超时时间为30秒,避免中途超时 volleyMultipartRequest.retryPolicy = DefaultRetryPolicy( 30000, DefaultRetryPolicy.DEFAULT_MAX_RETRIES, DefaultRetryPolicy.DEFAULT_BACKOFF_MULT ) rQueue.add(volleyMultipartRequest) } catch (e: FileNotFoundException) { e.printStackTrace() } catch (e: IOException) { e.printStackTrace() } } @Throws(IOException::class) fun getBytes(inputStream: InputStream?): ByteArray { val byteBuffer = ByteArrayOutputStream() val bufferSize = 1024 val buffer = ByteArray(bufferSize) var len = 0 while (inputStream!!.read(buffer).also { len = it } != -1) { byteBuffer.write(buffer, 0, len) } return byteBuffer.toByteArray() }
2. 完善文件选择结果处理逻辑
在onActivityResult中兼容单选、多选两种情况,收集所有选中的文件后调用批量上传函数:
attachbtn.setOnClickListener { val mimeTypes = arrayOf("image/*", "application/pdf") val intent = Intent() intent.type = "*/*" intent.action = Intent.ACTION_GET_CONTENT intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes) startActivityForResult(Intent.createChooser(intent, "选择文件"), 111) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { super.onActivityResult(requestCode, resultCode, data) if (requestCode == 111 && resultCode == Activity.RESULT_OK && data != null) { val fileList = mutableListOf<Pair<String, Uri>>() // 处理多选情况 if (data.clipData != null) { val mulfile = data.clipData!! val itemcount = mulfile.itemCount for (i in 0 until itemcount) { val uri = mulfile.getItemAt(i).uri // 可自行实现从Uri获取原文件名的逻辑,此处用时间戳作为示例 val fileName = "file_${System.currentTimeMillis()}_$i" fileList.add(Pair(fileName, uri)) } } // 处理单选情况 else if (data.data != null) { val uri = data.data!! val fileName = "file_${System.currentTimeMillis()}" fileList.add(Pair(fileName, uri)) } // 调用批量上传 if (fileList.isNotEmpty()) { uploadFiles(fileList) } } }
注意事项
- 多文件参数名需要和服务端约定的接收字段完全匹配,如果服务端要求接收数组格式的字段,可将参数名改为
evidence_image[]或者evidence_image[下标] - 如果需要上传的文件总大小过大,一次性将所有文件读入内存可能触发OOM,这种场景建议改用分块上传方案
- 可根据实际业务需求调整重试策略的超时时间和重试次数
内容的提问来源于stack exchange,提问作者Ashutosh Singh
相关产品推荐
相关产品推荐

