You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java通过Retrofit获取响应体PDF并实现直接预览

Android端Retrofit获取PDF并直接预览实现方案

问题场景

  • 技术栈:Java语言开发Android应用,使用Retrofit发起网络请求
  • 需求:调用POST接口获取application/pdf格式的文件流,直接在应用内预览,不需要将文件下载到用户可见的公共存储
  • 已验证:Postman发起带空数组[]请求体的POST请求可正常返回PDF流;Android端Retrofit已可成功请求接口,仅缺少从ResponseBody解析PDF并渲染的逻辑

原有代码问题

首先修正原有代码的两处错误,否则后续解析必然失败:

  1. 接口定义错误:原有接口未传入Postman测试时携带的[]空请求体,且错误将响应头的Content-Type: application/pdf写在了请求头位置
  2. Retrofit配置错误:添加了GsonConverterFactory,该转换器会尝试将二进制PDF流解析为JSON文本,直接导致流数据损坏

修正后的完整实现

1. 修正Retrofit接口定义

public interface WebApiEndPoint {
    @POST("MyEndpoint")
    Call<ResponseBody> getPdfStream(
            @Header("Authorization") String authToken,
            // 对应Postman中传递的[]空请求体
            @Body RequestBody emptyJsonBody
    );
}

2. 修正Retrofit请求逻辑

// 构造空JSON数组请求体
RequestBody emptyBody = RequestBody.create(MediaType.parse("application/json"), "[]");

new Retrofit.Builder()
        .baseUrl("http://localhost:5000/api/downlodeFile/")
        // 移除GsonConverterFactory,避免二进制流被破坏
        .build()
        .create(WebApiEndPoint.class)
        .getPdfStream(authToken, emptyBody)
        .enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
                if (!response.isSuccessful() || response.body() == null) {
                    Toast.makeText(getActivity(), "接口请求异常", Toast.LENGTH_SHORT).show();
                    return;
                }
                Toast.makeText(getActivity(), response.message(), Toast.LENGTH_SHORT).show();
                Log.i("PDF", "开始解析PDF流");
                // 传入响应体解析并渲染PDF
                parseAndRenderPdf(response.body());
            }

            @Override
            public void onFailure(Call<ResponseBody> call, Throwable t) {
                Toast.makeText(getActivity(), t.getMessage(), Toast.LENGTH_SHORT).show();
                Log.e("PDF", "PDF请求失败", t);
            }
        });

3. 实现PDF解析与渲染逻辑

使用Android原生自带的PdfRenderer实现预览,不需要引入第三方依赖,流写入应用私有缓存目录,用户无感知:

private void parseAndRenderPdf(ResponseBody responseBody) {
    // 流读写必须放在子线程,避免主线程阻塞ANR
    new Thread(() -> {
        ParcelFileDescriptor pfd = null;
        PdfRenderer pdfRenderer = null;
        try {
            // 创建应用私有缓存目录下的临时文件,用户不可见,系统自动清理
            File tempPdfFile = new File(requireContext().getCacheDir(), "temp_pdf_preview.pdf");
            if (tempPdfFile.exists()) tempPdfFile.delete();

            // 将响应流写入临时文件
            InputStream is = responseBody.byteStream();
            FileOutputStream fos = new FileOutputStream(tempPdfFile);
            byte[] buffer = new byte[4096];
            int readLen;
            while ((readLen = is.read(buffer)) != -1) {
                fos.write(buffer, 0, readLen);
            }
            fos.flush();
            is.close();
            fos.close();

            // 打开PDF文件渲染
            pfd = ParcelFileDescriptor.open(tempPdfFile, ParcelFileDescriptor.MODE_READ_ONLY);
            pdfRenderer = new PdfRenderer(pfd);
            
            // 示例:渲染PDF第一页,多页场景可循环openPage渲染所有页面
            PdfRenderer.Page firstPage = pdfRenderer.openPage(0);
            Bitmap pageBitmap = Bitmap.createBitmap(
                    firstPage.getWidth(),
                    firstPage.getHeight(),
                    Bitmap.Config.ARGB_8888
            );
            firstPage.render(pageBitmap, null, null, PdfRenderer.Page.RENDER_MODE_FOR_DISPLAY);
            firstPage.close();

            // 切回主线程更新UI
            requireActivity().runOnUiThread(() -> {
                // 将渲染好的Bitmap设置到布局中的ImageView即可展示
                // 多页场景可结合ViewPager2实现翻页,也可支持缩放等交互
                pdfPreviewIv.setImageBitmap(pageBitmap);
                Toast.makeText(getActivity(), "PDF加载完成", Toast.LENGTH_SHORT).show();
            });

        } catch (Exception e) {
            e.printStackTrace();
            requireActivity().runOnUiThread(() -> 
                Toast.makeText(getActivity(), "PDF解析失败:" + e.getMessage(), Toast.LENGTH_SHORT).show()
            );
        } finally {
            // 关闭资源避免泄漏
            try {
                if (pdfRenderer != null) pdfRenderer.close();
                if (pfd != null) pfd.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }).start();
}

注意事项

  • 必须在AndroidManifest.xml添加网络权限:<uses-permission android:name="android.permission.INTERNET" />
  • Android 9及以上系统默认禁止HTTP明文请求,若接口未使用HTTPS,需要在manifest的<application>标签添加android:usesCleartextTraffic="true"
  • PdfRenderer不支持直接读取输入流,必须落地为可随机访问的文件,写入私有缓存不属于用户感知的“下载到本地”,不会出现在系统下载目录中
  • 若需要更完善的PDF交互(缩放、翻页动画、书签等),可在流写入临时文件后,将文件路径传给第三方PDF预览组件,流处理逻辑无需修改

内容的提问来源于stack exchange,提问作者Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.01 22:12:51