簡體   English   中英

Xamarin Android WebView 下載內聯pdf內容

[英]Xamarin Android WebView download inline pdf content

Android 專家......我陷入了困境。 我在 Forms 應用程序中嵌入了一個 WebView,該應用程序還為它實現了一個自定義渲染器(用於 WebView)。

我正在封裝一個基本的 Web 應用程序 (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"

...現在,在該 url 后面,有一段 javascript 生成動態 PDF,因此它不會存儲在服務器上的任何位置,這就是我似乎遇到問題的地方。

我在網上閱讀的所有內容都說使用 HttpClient 將 PDF 的內容下載到文件中,然后打開它。 但是,如果我將該 URL 傳遞給 HttpClient,則不會獲取 PDF 數據,而是獲取 HTML 文本。

我已經在 StackOverflow 上嘗試了 GoogleDocs 和許多其他鏈接,但無論我做什么,該 URL 都會返回 HTML 文本,而不是 PDF 文檔的內容。

我對 SAP 給我的控制也很少,所以改變服務器端是一個不太可能的選擇。

任何幫助將不勝感激。

不幸的是,由於某種原因,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. 通過其他應用調用 Intent 以顯示 PDF
  3. 如果您使用 API 級別 21 及更高級別 - 您可以使用 Android PdfRenderer

希望有幫助

您可以通過兩種解決方案來做到這一點:

解決方案 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>     

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM