使用Retrofit上传PDF及图片文件,如何从URI获取PDF绝对路径
解决方案
无需获取PDF的绝对路径即可完成上传,Android 4.4及以上版本从系统文件选择器拿到的URI多为content://类型,通过ContentResolver直接读取文件流即可,不需要转换为本地真实路径。
步骤1:处理PDF选择返回结果,读取文件内容
在onActivityResult回调中处理选择PDF的返回结果,直接从URI读取PDF字节数组、获取文件名:
// 自定义的PDF选择请求码 private static final int SELECT_PDF = 1002; if (requestCode == SELECT_PDF) { List<byte[]> pdfDataList = new ArrayList<>(); List<String> pdfFileNameList = new ArrayList<>(); // 单选PDF场景 if (data.getData() != null) { Uri pdfUri = data.getData(); try { InputStream inputStream = getActivity().getContentResolver().openInputStream(pdfUri); byte[] pdfBytes = getBytesFromInputStream(inputStream); pdfDataList.add(pdfBytes); pdfFileNameList.add(getFileNameFromUri(pdfUri)); } catch (Exception e) { e.printStackTrace(); } } // 多选PDF场景 else if (data.getClipData() != null) { int count = data.getClipData().getItemCount(); for (int i = 0; i < count; i++) { Uri pdfUri = data.getClipData().getItemAt(i).getUri(); try { InputStream inputStream = getActivity().getContentResolver().openInputStream(pdfUri); byte[] pdfBytes = getBytesFromInputStream(inputStream); pdfDataList.add(pdfBytes); pdfFileNameList.add(getFileNameFromUri(pdfUri)); } catch (Exception e) { e.printStackTrace(); } } } // 调用上传方法 uploadPdf(pdfDataList, pdfFileNameList); }
步骤2:添加工具方法
添加两个通用工具方法,分别实现输入流转字节数组、从URI获取文件名:
// 输入流转字节数组 private byte[] getBytesFromInputStream(InputStream inputStream) throws IOException { ByteArrayOutputStream baos = new ByteArrayOutputStream(); byte[] buffer = new byte[1024]; int len; while ((len = inputStream.read(buffer)) != -1) { baos.write(buffer, 0, len); } inputStream.close(); return baos.toByteArray(); } // 从URI获取文件名,无需读真实路径 private String getFileNameFromUri(Uri uri) { String fileName = "unnamed.pdf"; Cursor cursor = getActivity().getContentResolver().query(uri, null, null, null, null); if (cursor != null && cursor.moveToFirst()) { int nameIndex = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME); fileName = cursor.getString(nameIndex); cursor.close(); } return fileName; }
步骤3:实现PDF上传逻辑
参考你已有的图片上传逻辑,调整MediaType和表单字段即可:
首先定义API接口(如果后端有单独的PDF上传接口,也可以直接复用你现有上传接口,只要后端支持)
@Multipart @POST("/api/pdf/upload") Call<UploadResponse> uploadPdf(@Header("x-auth-token") String authHeader, @Part List<MultipartBody.Part> pdfParts);
上传方法实现
private void uploadPdf(List<byte[]> pdfDataList, List<String> fileNameList) { MultipartBody.Part[] parts = new MultipartBody.Part[pdfDataList.size()]; for (int i = 0; i < pdfDataList.size(); i++) { // 修改MediaType为PDF类型 RequestBody requestFile = RequestBody.create(MediaType.parse("application/pdf"), pdfDataList.get(i)); String fileName = fileNameList.get(i); // 表单字段名需和后端约定一致,此处示例为pdf MultipartBody.Part body = MultipartBody.Part.createFormData("pdf", fileName, requestFile); parts[i] = body; } String token = sessionCache.getToken(); Call<UploadResponse> call = apiInterface.uploadPdf(token, Arrays.asList(parts)); call.enqueue(new Callback<UploadResponse>() { @Override public void onResponse(Call<UploadResponse> call, Response<UploadResponse> response) { Toast.makeText(getContext(), "PDF上传成功", Toast.LENGTH_LONG).show(); // 处理返回的文件路径等逻辑 } @Override public void onFailure(Call<UploadResponse> call, Throwable t) { // 处理上传失败逻辑 } }); }
注意事项
- 通过系统文件选择器获取的URI,应用有临时访问权限,不需要额外申请存储权限即可读取内容
- 如果后端接口同时支持上传图片和PDF,可将两类文件的MultipartBody.Part合并到同一个列表中,一次请求完成上传
- Android 13及以上版本如果需要扫描本地PDF列表,需申请
READ_MEDIA_DOCUMENTS权限,通过选择器选择文件无需额外权限
内容的提问来源于stack exchange,提问作者Hiccup
相关产品推荐
相关产品推荐

