user13714356
user13714356

Reputation: 171

Blazor Server Hybrid app download file to android does not work - using BlazorDownloadFile

I have a Blazor Server Hybrid app in .Net 7. I have my .razor componets built in a razor class library and I have two projects, one for web and one for Blazor MAUI. The Blazor MAUI app is pushing a build to my phone which is running Android. The app has a download image button, which takes a blob from a SQL table and pushes it to the user for downloading. I am using the NuGet package BlazorDownloadFile which works great for the web app - however it does not work on my Android device. They are both using the same .razor component - so the code is the same.

Any idea why this may happen. Could it be that I need to set some permissions on the mobile app to allow downloading of the file from an app (I thought it would prompt user if they want to download)? Or maybe something in my build for the mobile app version - allow this app to download files when using?

Any advice would be much appreciated as its my first mobile app

I also have the below permissions in my AndroidManifest.xml which all I think I need for my app to download a file from it self

    <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true"></application>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

I have looked at my Call Stack tab in Visual Studio after the button is clicked on my Android device. I cant see any type of error

I am running a phone that has Android 10, could this be the issue in that its old maybe? Is there anywhere else I should look for errors?

enter image description here

I have checked the Output tab and I think the issue is on line@ [libEGL] EGLNativeWindowType 0x6f4c0b8410 disconnect failed

Im not sure what this line is doing - any suggestions welcome. See below screen shot of full output

enter image description here

I have enabled the Diagnositic MSBuild Output, below is what I see.

enter image description here

I changed my download function to the below so it creates a new thread when doing the download but no joy. Same error msg

enter image description here

I added the code to my MainPage.xaml.cs file but I get an error when I try and build the app. It doesnt like the GetActivity method - see below. Any idea why this happens?

enter image description here

I used the updated MainPage.xaml.cs and it got rid of tge GetActivity() error which was great. But it still doesnt work on an Android device. I seem to be getting a Blob error now - see below?

enter image description here

My download button is in a Razor Class Library which is then shared between a Blazor Server project and also my Blazor Maui project, However when I created my Razor class library im not sure if I selected the "Support page and views" when I created the project. Could thsi be teh issue?

enter image description here

I have uploaded an example project which contains 3 projects:

  1. Razor class library which contains the Download Control
  2. Blazor Server App that will use the above Razor Class Library
  3. MAUI Blazor app that will use the Razor Class Library too

I may have got a bit confused by it all but I am hoping you get the drift of it. You can see teh code that I tried to applied from yoru demo: https://github.com/ig-isg/DownloadTest3App

UPDATE: 11 Apr 2023 - I found that the below kind of worked, I had a button click that called the below:

private async Task DownloadFile(int DocumentID) { var d = await documentService.GetDocumentAsync(DocumentID);

    string UploadPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "");
    string fullFilename = Path.Combine(UploadPath, d.Filename);
    using var writer = new BinaryWriter(File.OpenWrite(fullFilename));
    writer.Write(d.FileData);
}

But I noticed it does not prompt the user that the file has downloaded. Also it difficult to see where the file is actually stored. On my Android device it stores it under:

/data/user/0/com.companyname.myappname/files

But the above folder is hard to find on my Android device. I would like it to download the file to my Gallary folder on my phone. Any ideas how I do this?

Upvotes: 0

Views: 1229

Answers (1)

Alex Chow
Alex Chow

Reputation: 21

  1. i make a downloadFileFromStreamToDataUrl function to convent blob to DataUrl, and put filename to url like data:text/{filename}

    projet https://github.com/densen2014/BootstrapBlazor.WebAPI

  2. maui android use DataUrl2Bytes to save file.

my codes:

https://github.com/densen2014/BlazorMaui/blob/master/MauiApp_PdfReader/MainPage.xaml.cs

https://github.com/densen2014/BlazorMaui/blob/master/MauiApp_PdfReader/Pages/Downloads.razor

first

<PackageReference Include="BootstrapBlazor.WebAPI" Version="7.0.5" />

MainPage.xaml.cs

protected string UploadPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "uploads");

public MainPage()
{
    InitializeComponent();

    blazorWebView.BlazorWebViewInitialized += BlazorWebViewInitialized; 
}

private void BlazorWebViewInitialized(object? sender, BlazorWebViewInitializedEventArgs e)
{
...
 e.WebView.Download +=(async (s,e)=> await WebView_DownloadAsync(s,e));
...
}

#if ANDROID
    private async Task WebView_DownloadAsync(object sender, DownloadEventArgs e)
    {
        Uri uri = new Uri(e.Url);
        await DownloadAsync(uri, e.Mimetype);
    }
#endif
    private async Task DownloadAsync(Uri uri,string? mimeType=null)
    {
        string fileName = Path.GetFileName(uri.LocalPath);
        var httpClient = new HttpClient();
        var filePath = Path.Combine(UploadPath, fileName);
#if ANDROID
        if (uri.Scheme== "data")
        {
            fileName =DataUrl2Filename( uri.OriginalString);
            filePath = Path.Combine(UploadPath,$"{DateTime.Now.ToString("yyyy-MM-dd-hhmmss")}-{fileName}");
            var bytes = DataUrl2Bytes(uri.OriginalString);
            File.WriteAllBytes(filePath, bytes);
            await DisplayAlert("提示", $"下载文件完成 {fileName}", "OK");
            return;
        }
#endif 
        byte[] fileBytes = await httpClient.GetByteArrayAsync(uri);
        File.WriteAllBytes(filePath, fileBytes);
        await DisplayAlert("提示", $"下载文件完成 {fileName}", "OK");
    }

    public static string DataUrl2Filename(string base64encodedstring)
    {
        var filename = Regex.Match(base64encodedstring, @"data:text/(?<filename>.+?);(?<type2>.+?),(?<data>.+)").Groups["filename"].Value;
        return filename;
    }

    /// <summary>
    /// 从 DataUrl 转换为 Stream
    /// <para>Convert from a DataUrl to an Stream</para>
    /// </summary>
    /// <param name="base64encodedstring"></param>
    /// <returns></returns>
    public static byte[] DataUrl2Bytes(string base64encodedstring)
    {
        var base64Data = Regex.Match(base64encodedstring, @"data:text/(?<type>.+?),(?<data>.+)").Groups["data"].Value;
        var bytes = Convert.FromBase64String(base64Data); 
        return bytes;
    } 

then razor

<DownloadBlob OnError="@OnError" @ref="DownloadBlob" />

<button class="btn btn-primary" @onclick="(async()=>await DownloadTest())">Download File From Stream</button>


@code{

    protected string UploadPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "uploads");

    string? Message { get; set; }

    [System.Diagnostics.CodeAnalysis.NotNull]
    DownloadBlob? DownloadBlob { get; set; }

#if ANDROID
    bool isAndroid = true;
#else
    bool isAndroid = false;
#endif


    private Task OnError(string message)
    {
        this.Message = message;
        StateHasChanged();
        return Task.CompletedTask;
    }

    private async Task DownloadTest()
    {
        //生成随机文本内存流
        var stream = new MemoryStream(System.Text.Encoding.Default.GetBytes($"Log from blazor {DateTime.Now:F}"));
        var filename= $"test_{Guid.NewGuid()}.txt";
        this.Message = await DownloadBlob.DownloadFileFromStream(filename, stream, isAndroid);
        _ = Task.Run (async() =>
        {
            await Task.Delay(500);
            await InvokeAsync(StateHasChanged);
        });
    }

}

Upvotes: 0

Related Questions