在Android WebView中从Base64 URL下载文件

11

我正在编写一个Webview应用程序,在其中可以将文件从(HTML标签)URL下载到设备。我可以下载像PNG / JPG / PDF等文件,但当URL是Base64字符串值时,我不知道如何下载它。有人可以帮我实现吗?

例如,当单击下面的HTML链接时,文件abc.png可以轻松下载

<a href="http://web.com/abc.png" download >Download</a>

但是当URL像下面这样是base64编码时,webview无法下载“文件”:


<a href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIA..." download>Download</a>

@DimaKozhevin 不是同一个问题。我不想将图像加载到Web视图中,我想将Base64转换为文件并保存到设备。 - Elnoor
3个回答

20

我成功地将base64编码数据保存为文件。因此,对于我的问题,基本的简短答案是将编码数据解码为字节并像这样将其写入文件:

String base64EncodedString = encodedDataUrl.substring(encodedDataUrl.indexOf(",") + 1);
byte[] decodedBytes = Base64.decode(base64EncodedString, Base64.DEFAULT);
OutputStream os = new FileOutputStream(file);
os.write(decodedBytes);
os.close();
仅供其他可能遇到相同问题的人参考,我在下面添加了我的最终代码。在 onCreate() 方法中,我像这样处理文件下载:
webView.setDownloadListener(new DownloadListener() {
    @Override
    public void onDownloadStart(String url, String userAgent,
                                String contentDisposition, String mimeType,
                                long contentLength) {

        if (url.startsWith("data:")) {  //when url is base64 encoded data
            String path = createAndSaveFileFromBase64Url(url);
            return;
        }

        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(url));
        request.setMimeType(mimeType);
        String cookies = CookieManager.getInstance().getCookie(url);
        request.addRequestHeader("cookie", cookies);
        request.addRequestHeader("User-Agent", userAgent);
        request.setDescription(getResources().getString(R.string.msg_downloading));
        String filename = URLUtil.guessFileName(url, contentDisposition, mimeType);
        request.setTitle(filename);
        request.allowScanningByMediaScanner();
        request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
        request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);
        DownloadManager dm = (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        dm.enqueue(request);
        Toast.makeText(getApplicationContext(), R.string.msg_downloading, Toast.LENGTH_LONG).show();
    }
});

处理Base64编码数据的createAndSaveFileFromBase64Url()方法如下:

public String createAndSaveFileFromBase64Url(String url) {
        File path = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS);
        String filetype = url.substring(url.indexOf("/") + 1, url.indexOf(";"));
        String filename = System.currentTimeMillis() + "." + filetype;
        File file = new File(path, filename);
        try {
            if(!path.exists())
                path.mkdirs();
            if(!file.exists())
                file.createNewFile();

            String base64EncodedString = url.substring(url.indexOf(",") + 1);
            byte[] decodedBytes = Base64.decode(base64EncodedString, Base64.DEFAULT);
            OutputStream os = new FileOutputStream(file);
            os.write(decodedBytes);
            os.close();

            //Tell the media scanner about the new file so that it is immediately available to the user.
            MediaScannerConnection.scanFile(this,
                    new String[]{file.toString()}, null,
                    new MediaScannerConnection.OnScanCompletedListener() {
                        public void onScanCompleted(String path, Uri uri) {
                            Log.i("ExternalStorage", "Scanned " + path + ":");
                            Log.i("ExternalStorage", "-> uri=" + uri);
                        }
                    });

            //Set notification after download complete and add "click to view" action to that
            String mimetype = url.substring(url.indexOf(":") + 1, url.indexOf("/"));
            Intent intent = new Intent();
            intent.setAction(android.content.Intent.ACTION_VIEW);
            intent.setDataAndType(Uri.fromFile(file), (mimetype + "/*"));
            PendingIntent pIntent = PendingIntent.getActivity(this, 0, intent, 0);

            Notification notification = new NotificationCompat.Builder(this)
                                                        .setSmallIcon(R.mipmap.ic_launcher)
                                                        .setContentText(getString(R.string.msg_file_downloaded))
                                                        .setContentTitle(filename)
                                                        .setContentIntent(pIntent)
                                                        .build();

            notification.flags |= Notification.FLAG_AUTO_CANCEL;
            int notificationId = 85851;
            NotificationManager notificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationManager.notify(notificationId, notification);
        } catch (IOException e) {
            Log.w("ExternalStorage", "Error writing " + file, e);
            Toast.makeText(getApplicationContext(), R.string.error_downloading, Toast.LENGTH_LONG).show();
        }

        return file.toString();
    }

1
你的代码出现了以下错误,你能帮我解决一下吗? android.os.FileUriExposedException: file:///storage/emulated/0/Download/1548198938835.png 通过 Intent.getData() 暴露在应用程序之外 at android.os.StrictMode.onFileUriExposed(StrictMode.java:1799) at android.net.Uri.checkFileUriExposed(Uri.java:2346) at android.content.Intent.prepareToLeaveProcess(Intent.java:8965) at android.content.Intent.prepareToLeaveProcess(Intent.java:8926) at android.app.PendingIntent.getActivity(PendingIntent.java:340) - Lenin Meza
2
@LeninMeza 这可能有帮助 https://dev59.com/zVoT5IYBdhLWcg3wvRpk - Elnoor

0
如果您正在使用Java 1.8,您可以尝试使用以下代码:
     import java.util.Base64;

     public class DecodeBase64 {

     public static void main(String []args){
        String encodedUrl = "aHR0cHM6Ly9zdGFja292ZXJmbG93LmNvbS9xdWVzdGlvbnMvNDY1NzkyMzcvZG93bmxvYWQtZmlsZS1mcm9tLWJhc2U2NC11cmwtaW4tYW5kcm9pZC13ZWJ2aWV3LzQ2NTc5MzE0";
        byte[] decodedBytes = Base64.getUrlDecoder().decode(encodedUrl);
        String result = new String(decodedBytes);
        System.out.println(result);
     }
}

输出应该是:

https://dev59.com/EVYN5IYBdhLWcg3w-cnx#46579314


谢谢,但是我在解码base64并创建位图之后应该做什么? - Elnoor
你需要将字节数组转换为字符串,我修改了上面的片段。 - Alex Gonzalez
必须添加getUrlDecoder()函数以获取URL和文件名安全解码器。 - Alex Gonzalez
它不是图像的URL编码,而是文件本身以Base64编码。基本上,它是在网站中使用JavaScript动态生成的文件,并将创建的文件的Base64代码写入HTML <a>标签的href中,然后单击该标签,启动文件下载。 - Elnoor

0

首先,您需要将此base64字符串转换为位图图像格式,然后尝试下载。

您可以像这样转换字符串。

public static Bitmap decodeBase64(String input)
{
    byte[] decodedByte = Base64.decode(input, Base64.DEFAULT); 


    return BitmapFactory.decodeByteArray(decodedByte, 0, decodedByte.length);
}

它将返回您的base64位图。


好的,在我创建了位图之后,我应该怎么做? - Elnoor
你可以将这个位图设置在任何隐藏的ImageView上,像这样:imageView.setImageBitmap(bitmap);之后,你可以像以前一样轻松地下载它。 - Tejas Pandya
抱歉,我还没明白。对于其他文件,通常会有一个像这样的HTML标签:<a download href="image.png" >download</a>,点击它将会下载该文件。 - Elnoor
正如您在问题中提到的,您可以从HTML标签下载文件。现在有什么问题吗? - Tejas Pandya
如果URL包含文件路径/名称,我可以下载它,但如果URL是Base64编码的,我就无法下载。就是这么简单。 - Elnoor
在按照我提到的步骤之后,您需要访问以下链接:'https://stackoverflow.com/questions/38820673/saving-an-image-from-imageview-into-internal-storage' - Tejas Pandya

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接