java - 下载 PDF 并显示它 (FileUriExposedException)

标签 java android android-studio pdf

作为前言,我熟悉编码(高中 2 年),但对于 Android Studio(以及一般而言的 Java)完全是新手。我一直在寻找问题的解决方案,但由于我缺乏经验,我不知道如何在我的项目中实现这些解决方案。

简而言之,我需要从某个外部 URL 下载 pdf,将其存储到外部存储中,然后使用某个 pdf 查看器应用程序显示它。我收到错误:

...

android.os.FileUriExposedException: file:///storage/emulated/0/pdf/Read.pdf exposed beyond app through Intent.getData()

...

我一直在使用this source on using an intent to open a pdf-viewerthis source on "File Provider"作为引用。

以下是我目前所掌握的内容:

Fire(带有可点击的TextView,应下载并显示 pdf)

public class Fire extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_fire);
    final TextView testButton = findViewById(R.id.testPDF);
    // File file = getFilesDir();

    testButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            Intent intent = new Intent(Fire.this, pdfView.class);
            startActivity(intent);
        }
    });

pdf查看 Activity

public class pdfView extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_pdf_view);
    String extStorageDirectory = Environment.getExternalStorageDirectory().toString();
    File folder = new File(extStorageDirectory, "pdf");
    folder.mkdir();
    File file = new File(folder, "Read.pdf");
    try {
        file.createNewFile();
    } catch (IOException e1) {
        e1.printStackTrace();
    }
    Downloader.DownloadFile(__PDF_URL___, file);

    showPdf();
}
public void showPdf()
{
    File file = new File(Environment.getExternalStorageDirectory()+"/pdf/Read.pdf");
    PackageManager packageManager = getPackageManager();
    Intent testIntent = new Intent(Intent.ACTION_VIEW);
    testIntent.setType("application/pdf");
    List list = packageManager.queryIntentActivities(testIntent, PackageManager.MATCH_DEFAULT_ONLY);
    Intent intent = new Intent();
    intent.setAction(Intent.ACTION_VIEW);
    Uri uri = Uri.fromFile(file);
    intent.setDataAndType(uri, "application/pdf");
    startActivity(intent);
}

下载器类

public class Downloader {

public static void DownloadFile(String fileURL, File directory) {
try {

        FileOutputStream f = new FileOutputStream(directory);
        URL u = new URL(fileURL);
        HttpURLConnection c = (HttpURLConnection) u.openConnection();
        c.setRequestMethod("GET");
        c.setDoOutput(true);
        c.connect();

        InputStream in = c.getInputStream();

        byte[] buffer = new byte[1024];
        int len1 = 0;
        while ((len1 = in.read(buffer)) > 0) {
            f.write(buffer, 0, len1);
        }
        f.close();
    } catch (Exception e) {
        e.printStackTrace();
    }

}

根据我的研究,我的问题似乎源于我使用 URI 而不是“文件提供程序”。另外,实现“文件提供程序”的解决方案似乎以某种形式使用上下文,我对上下文的目的是什么以及如何实现它感到困惑。

如果您没有答案也没关系。任何有关如何解决这个问题甚至理解这个概念的信息对我来说就足够了。

最佳答案

如果您的 targetSdkVersion >= 24,那么我们必须使用 FileProvider 类来授予对特定文件或文件夹的访问权限,以便其他应用程序可以访问它们。

1)首先在AndroidManifest.xml标签下添加FileProvider标签,如下:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    ...
    <application
        ...
        <provider
            android:name=".GenericFileProvider"
            android:authorities="${applicationId}.my.package.name.provider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths"/>
        </provider>
    </application>
</manifest>

2) 然后在 res/xml 文件夹中创建一个provider_paths.xml 文件。如果文件夹不存在,则可能需要创建。

<paths>
    <external-path name="external_files" path="."/>
</paths>

3) 现在创建 PdfDownload.java 类文件并粘贴以下代码:

public class PdfDownload extends Activity {
    TextView tv_loading;
    Context context;

    int downloadedSize = 0, totalsize;
    float per = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        isStoragePermissionGranted();
        super.onCreate(savedInstanceState);

        tv_loading = new TextView(this);
        tv_loading.setGravity(Gravity.CENTER);
        tv_loading.setTypeface(null, Typeface.BOLD);
        setContentView(tv_loading);
        downloadAndOpenPDF();
    }


    public static String getLastBitFromUrl(final String url) {
        return url.replaceFirst(".*/([^/?]+).*", "$1");
    }

    void downloadAndOpenPDF() {
        final String download_file_url = getIntent().getStringExtra("url");
        new Thread(new Runnable() {
            public void run() {
                Uri path = Uri.fromFile(downloadFile(download_file_url));
                try {
                    Intent intent = new Intent(Intent.ACTION_VIEW);
                    intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    Uri uri = FileProvider.getUriForFile(PdfDownload.this, BuildConfig.APPLICATION_ID, downloadFile(download_file_url));
                    intent.setDataAndType(uri, "application/pdf");
                    startActivity(intent);
                    finish();


                } catch (ActivityNotFoundException e) {
                    tv_loading
                            .setError("PDF Reader application is not installed in your device");
                }
            }
        }).start();

    }

    File downloadFile(String dwnload_file_path) {
        File file = null;
        try {
            URL url = new URL(dwnload_file_path);
            HttpURLConnection urlConnection = (HttpURLConnection) url
                    .openConnection();

            urlConnection.connect();

            String test = getLastBitFromUrl(dwnload_file_path);
            String dest_file_path = test.replace("%20", "_");
            // set the path where we want to save the file
            File SDCardRoot = Environment.getExternalStorageDirectory();
            // // create a new file, to save the downloaded file
            file = new File(SDCardRoot, dest_file_path);
            if (file.exists()) {
                return file;
            }
            FileOutputStream fileOutput = new FileOutputStream(file);

            // Stream used for reading the data from the internet
            InputStream inputStream = urlConnection.getInputStream();

            // this is the total size of the file which we are
            // downloading
            totalsize = urlConnection.getContentLength();
            setText("Starting PDF download...");

            // create a buffer...
            byte[] buffer = new byte[1024 * 1024];
            int bufferLength = 0;

            while ((bufferLength = inputStream.read(buffer)) > 0) {
                fileOutput.write(buffer, 0, bufferLength);
                downloadedSize += bufferLength;
                per = ((float) downloadedSize / totalsize) * 100;
                if ((totalsize / 1024) <= 1024) {
                    setText("Total PDF File size  : " + (totalsize / 1024)
                            + " KB\n\nDownloading PDF " + (int) per + "% complete");
                } else {
                    setText("Total PDF File size  : " + (totalsize / 1024) / 1024
                            + " MB\n\nDownloading PDF " + (int) per + "% complete");
                }
                // setText("configuring your book pleease wait a moment");
            }
            // close the output stream when complete //
            fileOutput.close();
            // setText("Download Complete. Open PDF Application installed in the device.");
            setText("configuaration is completed now your book is ready to read");
        } catch (final MalformedURLException e) {
            setTextError("Some error occured. Press back and try again.",
                    Color.RED);
        } catch (final IOException e) {
            setTextError("Some error occured. Press back and try again.",
                    Color.RED);
        } catch (final Exception e) {
            setTextError(
                    "Failed to download image. Please check your internet connection.",
                    Color.RED);
        }
        return file;
    }

    void setTextError(final String message, final int color) {
        runOnUiThread(new Runnable() {
            public void run() {
                tv_loading.setTextColor(color);
                tv_loading.setText(message);
            }
        });
    }

    void setText(final String txt) {
        runOnUiThread(new Runnable() {
            public void run() {
                tv_loading.setText(txt);
            }
        });

    }

    private static final String TAG = "MyActivity";

    public boolean isStoragePermissionGranted() {
        if (Build.VERSION.SDK_INT >= 23) {
            if (checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                    == PackageManager.PERMISSION_GRANTED) {
                Log.v(TAG, "Permission is granted");
                return true;
            } else {

                Log.v(TAG, "Permission is revoked");
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
                return false;
            }
        } else { //permission is automatically granted on sdk<23 upon installation
            Log.v(TAG, "Permission is granted");
            return true;
        }


    }


    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            Log.v(TAG, "Permission: " + permissions[0] + "was " + grantResults[0]);

        }
    }

}

关于java - 下载 PDF 并显示它 (FileUriExposedException),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48756035/

相关文章:

java - 为什么不可变对象(immutable对象)允许遵守里氏替换原则?

java - 如何在 JpaRepository 中使用升序降序

android - 如何将背景添加到 MPAndroidChart xAxis 标签?

android - Gradle 与绿道

android - 如何通过链接(而不是复制)使应用程序使用其自身文件夹之外的模块?

java - 在 HttpClient 4.0 中设置 HttpParams 对象

java - glassfish 更新后缺少 javax.enterprise.context 包

android - 使用陀螺仪构建一个简单的 Android 360 视频播放应用程序

java - 如何在另一个 Activity 的 fragment 中单击按钮时在另一个 Activity 中打开包含 webViews 的 fragment ?

android - 我不记得我的 android debug.keystore 密码