Xamarin Android WebView下载内联pdf内容

时间:2018-02-06 00:48:43

标签: android forms pdf xamarin webview

Android专家......我处在一个quardry中。我有一个嵌入在Forms应用程序中的WebView,它还具有为其实现的自定义渲染器(用于WebView)。

我正在包装一个基本的网络应用程序(SAP Fiori),并且在该应用程序中,它有一个工资声明部分,用户可以在其中查看他们的工资单。在该页面上,有一个按钮可将付款单下载到PDF。

在我的应用程序的iOS中,这一切都是标准的,即PDF打开并在浏览器中查看。在Android和iOS上,任何浏览器(如Chrome或Safari)都可以正常工作。

在我的Android应用中,点击"以PDF格式打开"按钮没有结果,前端没有任何事情发生但在应用程序本身内,触发了实现的DownloadListener方法......

protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.WebView> e)
    {
        base.OnElementChanged(e);

        Control.Settings.AllowUniversalAccessFromFileURLs = true;

        Control.SetWebViewClient(new MySAPWebViewClient(Context));
        Control.SetDownloadListener(new MySAPDownloadListener());
    }

public class MySAPDownloadListener : Java.Lang.Object, IDownloadListener
{
    public new void Dispose() { }

    public void OnDownloadStart(string url, string userAgent, string contentDisposition, string mimetype, long contentLength)
    {
        System.Diagnostics.Debug.WriteLine("Url = " + url);
        System.Diagnostics.Debug.WriteLine("Content Disposition = " + contentDisposition);
        System.Diagnostics.Debug.WriteLine("Mime Type = " + mimetype);
    }
}

这些是主要的DownloadEventArgs值...

url = "https://organisation.com/sap/opu/odata/sap/HCM_MY_PAYSTUBS_SRV/PDFPaystubs(SEQUENCENUMBER=189,PersonnelAssignment='00044411')/$value"
contentDisposition = "inline; filename=Paystub_01.31.2018.pdf"
mimetype = "application/pdf"

...现在,在该网址后面,有一段javascript会生成动态PDF,因此它不会存储在服务器上的任何位置,这就是我似乎遇到问题的地方。

我在网上阅读的所有内容都说使用HttpClient将PDF的内容下载到文件然后打开它。但是,如果我将该URL传递给HttpClient,我无法获取PDF数据,我会收到HTML文本。

我在StackOverflow上尝试了GoogleDocs和许多其他链接,但无论我做什么,该URL都会返回HTML文本,而不是PDF文档的内容。

我几乎无法控制SAP给我的东西,所以更改服务器端是不太可能的选择。

非常感谢任何帮助。

2 个答案:

答案 0 :(得分:0)

不幸的是,由于某些原因,Android WebView无法显示PDF文件。 因此,如果使用WebView对您来说至关重要,那么只有一个选项 - 使用Google Docs Viewer。但它仅适用于通过公共URL提供的PDF。它看起来像这样:

 WebView view=(WebView)findViewById(R.id.webView);
 view.setWebViewClient(new WebViewClient());
 String url=getIntent().toString("value1");
 String URL="https://docs.google.com/viewer?url="+url; 
 view.loadUrl(Url);

如果使用WebView不是那么重要,可以使用:

  1. 某些显示PDF的第三方库
  2. 通过其他应用程序调用意图显示PDF
  3. 如果您使用的是API级别21及以上 - 您可以轻松使用Android PdfRenderer
  4. 希望有所帮助

答案 1 :(得分:0)

您可以通过两种解决方案来做到这一点:

解决方案1:在“系统默认浏览器”中打开URL。

private async Task MWebview_Download(object sender, DownloadEventArgs e)
{
    await Launcher.OpenAsync(e.Url);               
}

解决方案2 使用HttpClient下载。

    private async void MWebview_Download(object sender, DownloadEventArgs e)
    {
        string fileName = GetFileName(e);

        Device.BeginInvokeOnMainThread(() =>
        {
            Toast.MakeText(MainActivity.Instance, "Downloading File...", ToastLength.Long).Show();
        });


        await DownloadPdfFile(e, fileName);
    }

    private async Task DownloadPdfFile(DownloadEventArgs e, string fileName)
    {
        try
        {
            //Bypass the certificate 
            HttpClientHandler clientHandler = new HttpClientHandler();
            clientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => { return true; };

            // Getting Cookie
            String cookies = CookieManager.Instance.GetCookie(e.Url);

            var client = new HttpClient(clientHandler);
            var httpRequestMessage = new HttpRequestMessage
            {
                Method = HttpMethod.Get,
                RequestUri = new Uri(e.Url),
                Headers = {
                { HttpRequestHeader.TransferEncoding.ToString(), "binary" },
                { HttpRequestHeader.ContentLength.ToString(), e.ContentLength+"" },
                { HttpRequestHeader.ContentType.ToString(), "application/pdf" },
                {HttpRequestHeader.Cookie.ToString(),cookies },
            },
            };

            // Api Call
            var response = await client.SendAsync(httpRequestMessage);

            // Read content as Byte[]
            var bytes = response.Content.ReadAsByteArrayAsync().Result;

            // Save to Local DB
            var documentsPath = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);
            var filePath = Path.Combine(documentsPath, fileName);

            // convert byte to stream
            MemoryStream stream = new MemoryStream(bytes);

            // Write file
            SaveAndView(fileName, "application/pdf", stream);

        }
        catch (Exception ee)
        {
            Toast.MakeText(MainActivity.Instance, "Failed !!", ToastLength.Long).Show();
        }
    }

    public void SaveAndView(string fileName, String contentType, MemoryStream stream)
    {
        string exception = string.Empty;
        string root = null;

        if (ContextCompat.CheckSelfPermission(MainActivity.Instance, Manifest.Permission.WriteExternalStorage) != Permission.Granted)
        {
            ActivityCompat.RequestPermissions((Activity)MainActivity.Instance, new String[] { Manifest.Permission.WriteExternalStorage }, 1);
        }

        if (Android.OS.Environment.IsExternalStorageEmulated)
        {
            root = Android.OS.Environment.ExternalStorageDirectory.ToString();
        }
        else
            root = System.Environment.GetFolderPath(System.Environment.SpecialFolder.MyDocuments);

        Java.IO.File myDir = new Java.IO.File(root + "/PDFFiles");
        myDir.Mkdir();

        Java.IO.File file = new Java.IO.File(myDir, fileName);

        if (file.Exists()) file.Delete();

        try
        {
            FileOutputStream outs = new FileOutputStream(file);
            outs.Write(stream.ToArray());

            outs.Flush();
            outs.Close();
        }
        catch (Exception e)
        {
            exception = e.ToString();
        }

        if (file.Exists() && contentType != "application/html")
        {
            string extension = MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString());
            string mimeType = MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension);
            Intent intent = new Intent(Intent.ActionView);
            intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask);
            Android.Net.Uri path = FileProvider.GetUriForFile(MainActivity.Instance, Android.App.Application.Context.PackageName + ".provider", file);
            intent.SetDataAndType(path, mimeType);
            intent.AddFlags(ActivityFlags.GrantReadUriPermission);

            MainActivity.Instance.StartActivity(Intent.CreateChooser(intent, "Choose App"));
        }

    }

注意::确保处理FileProvider:

 **Changes in Manifest file:** 

   - Add android:requestLegacyExternalStorage="true" in application tag
   
   - Add between application tag

    <provider
        android:name="android.support.v4.content.FileProvider"
        android:authorities="${applicationId}.provider"
        android:exported="false" android:grantUriPermissions="true">
          <meta-data
             android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/file_paths" />
    </provider>

在Xamarin.Android项目的Resources文件夹内的xml文件夹中添加file_paths.xml:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
   <external-files-path name="my_images" path="Pictures" />
   <external-files-path name="my_movies" path="Movies" />
   <external-path name="external_files" path="."/>
</paths>