save excel file in device and send file as attachment

Hi All,
I am new to Xamarin and stuck with something. Please get me an idea how to solve this...

Details:
1. Create and Save a Excel file (I have successfully created one by using ISave and Xamarin.Forms.DependencyService.Get<ISave>().SaveAndView("GettingStared.xlsx", "application/msexcel", stream);
2. I dont get any error message and hence I assume that it has been created in my root directory 
3. Then I am trying to either attach that file as an email attachment (UNABLE TO DO SO ???)
4. I have an exercise of converting my data in a ObservableCollection into a CSV file and attached that file into email and successfully sent an email after attaching the same csv file. I have also verified my mailbox and the csv file has been attached successfully in my email mailbox
5. Now, I am trying the same with excel file. I have used XLSIO library of SyncFusion and done the coding but can not attach the excel file

My main objective:
I have a standalone application which allows user to upload an excel file. Once a file is choosen by user, the application successfully reads the data and shows in a form. Then user performs editing the data and after all edit is done, then I want the user to save the data back in somewhere. The only way I did was to send an email by converting my latest data into a csv file and attach that file in email and allow user to send that email. I have attached my gmail account to do so and everything works. But I want to extend my solution by playing with excel. I can successfully read the excel file and load data from 3 sheets. Then user edits the data and my final data is in my observable collection. Then while creating a excel file dynamically and attaching as an attachment, I FAILED. I just want to save the data back to my storage somehow or send a copy in an email attachment.


Any help will be much appreciated pleaseeee.
Kind regards

15 Replies 1 reply marked as answer

SK Shamini Kiruba Sobers Syncfusion Team July 20, 2020 08:15 AM UTC

Hi Bhabani, 

Greetings from Syncfusion. 

XlsIO doesn’t have support to attach files as an email attachment. Kindly try Xamarin.Essentials to do so. 

You can save the edited Excel file to your storage successfully with the help XlsIO, which is shown in the following UG link. The saved Excel file can be sent as an attachment in a mail using Xamarin.Essentials. 


Please let us know if this helps. 


Regards, 
Shamini 



BS Bhabani Sahoo July 21, 2020 10:18 PM UTC

Thanks Shamini for the quick answer. Much appreciated.

Can you please answer below?
I want to save the excel file to my local drive in android device and the write permission is also set in manifest file. When i ran below code after setting up all required things, it ran successfully. But i could not physically goto my device storage and spot that file at all. I simply want that the file i saved should be stored in any location so that i can later view the file manually/physically without using the app.
using ISave and Xamarin.Forms.DependencyService.Get<ISave>().SaveAndView("GettingStared.xlsx", "application/msexcel", stream);

Or is it possible that after i prepared the file, the ap should ask me/user where to save the file? That will also solve my problem. 

Whichever option works with xlsio library, i just want to save my newly created file to be able to save to the device so that I or the user of the android phone be able to view the physical file later inside the device’s storage somewhere.

Please could you help and guide me in right direction?
Kind regards


KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team July 22, 2020 12:21 PM UTC

Hi Bhabani, 

Thanks for the update. 

This is to inform you that it is not possible to choose the storage location at run-time. The root path in Android device is mentioned in SaveAndroid.cs file as, 

Code Snippet: 

//Get the root path in android device. 
if (Android.OS.Environment.IsExternalStorageEmulated) 
    root = Android.OS.Environment.ExternalStorageDirectory.ToString(); 
else 
    root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 

But, you can change the Android Environment to your requirement. You need to enable the storage permission for App in your Android Device, to spot the file. 

Kindly try this and let us know if the suggestion helps. 

Regards, 
Keerthi. 



BS Bhabani Sahoo July 23, 2020 10:28 PM UTC

Hi,
Thanks again. I managed to get deeper this time. (Pls refer the screen capture). I have checked thoroughly that I have granted READ/Write permission in Manifest XML file. But the code still fails because it has not got permission still. I went to device's app management manually to see if my app has permission over storage and yes its all there.

Then I used the plugin (https://forums.xamarin.com/discussion/158119/cant-find-this-folder-on-my-android-emulator) to check if I have permission or not and found that the permission is denied. I wonder what else can I do to enable permission? Am I missing something?

here is the code (it throws exception at FileOutputStream outs = new FileOutputStream(file); saying that no file found. in fact it does not create a folder even if it successfully executed that make directory line and did not throw error):

string root = null;
            //Get the root path in android device.
            if (Android.OS.Environment.IsExternalStorageEmulated)
            {
                root = Android.OS.Environment.ExternalStorageDirectory.ToString();
            }
            else
                root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);


            //Create directory and file 
            Java.IO.File myDir = new Java.IO.File(root + "/Syncfusion12");
            myDir.Mkdir();

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

            //Remove if the file exists
            if (file.Exists())
                file.Delete();

            //Write the stream into the file
            try
            {
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(stream.ToArray());
                outs.Flush();
                outs.Close();

            }
            catch (Exception ex)
                {
                string a = ex.Message;

            }

Attachment: Screenshot_20200723_at_23.16.06_26754439.zip


KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team July 24, 2020 11:37 AM UTC

Hi Bhabani, 

Thanks for the update. 

This is to inform you that a new runtime permission model is introduced for the Android SDK version 23 and above. So, include the following code for enabling the Android file provider to save and view the generated PDF document. 

  1. Create a new XML file with the name of provider_paths.xml under the Android project Resources folder and add the following code in it.
Eg: Resources/xml/provider_paths.xml 
 
Code Snippet:  

<?xml version="1.0" encoding="UTF-8" ?> 
  <external-path name="external_files" path="."/> 
</paths> 

  1. Add the following code to the AndroidManifest.xml file located under Properties/AndroidManifest.xml.

Code Snippet: 

<?xml version="1.0" encoding="utf-8"?> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.f156204_xamarin" android:installLocation="auto"> 
              <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28" /> 
              <application android:label="F156204_Xamarin.Android"> 
                             <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/provider_paths" /> 
                             </provider> 
              </application> 
              <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> 
              <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" /> 
              <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> 
</manifest> 

  1. You need to enable the storage permission for App in your Android Device, as mentioned in our previous update.

We have created a simple working sample for your reference and the same can be downloaded from the following link. 


Kindly try this and let us know if it helps.  

Regards, 
Keerthi. 


Marked as answer

BS Bhabani Sahoo July 24, 2020 03:39 PM UTC

Hi Keerthi,
I really appreciate your time helping me. Unfortunately, my progress is going like a snail.

1. Thanks for sending the sample link but it doesn't allow me to download/open. 

2. I did exactly what you said and tried. Now, the code successfully creates (I assume) as it doesn't throw error anymore for the step - Java.IO.File file = new Java.IO.File(root1, fileName);. However, I can't view the file by browsing the filesystem of the device.
3. Then I tried to add another step to save to MyDocuments just as a test. Then I went to the MyDocuments folder but still can't see the file. But strangely, when I rerun the app, it goes to the step where it says Delete file if it exists. That indicates (may be) the file has been created but hidden from me viewing. How to make that public instead of private?

4. Attached few screenshots, just for you to understand issues that I am facing

string root = null;
            //Get the root path in android device.
            if (Android.OS.Environment.IsExternalStorageEmulated)
            {
                root = Android.OS.Environment.ExternalStorageDirectory.ToString();
            }
            else
                root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

        string root1 = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
        //Create directory and file 
        Java.IO.File myDir = new Java.IO.File(root1 + "/Syncfusion12");
          myDir.Mkdir();

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

        //Remove if the file exists
        if (file.Exists())
                file.Delete();

            //Write the stream into the file
            try
            {
                FileOutputStream outs = new FileOutputStream(file);
                outs.Write(stream.ToArray());
                outs.Flush();
                outs.Close();

            }
            catch (Exception ex)
                {
                string a = ex.Message;

            } 

Kind regards,
Bhabani

Attachment: Archive_2ea647cf.zip


KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team July 27, 2020 11:21 AM UTC

Hi Bhabani, 

Thanks for the update. 

We have uploaded the working sample again and you can download it from below. 

Please find the code snippet used in SaveAndroid.cs file below. 

Code Snippet:  

public async Task SaveAndView(string fileName, String contentType, MemoryStream stream) 
    string exception = string.Empty; 
    string root = null; 
    //Get the root path in android device. 
    if (Android.OS.Environment.IsExternalStorageEmulated) 
    { 
        root = Android.OS.Environment.ExternalStorageDirectory.ToString(); 
    } 
    else 
        root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); 

    //Create directory and file  
    Java.IO.File myDir = new Java.IO.File(root + "/Syncfusion"); 
    myDir.Mkdir(); 

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

    //Remove if the file exists 
    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(); 
    } 

    Android.Net.Uri path = Android.Net.Uri.FromFile(file); 
    string extension = Android.Webkit.MimeTypeMap.GetFileExtensionFromUrl(Android.Net.Uri.FromFile(file).ToString()); 
    string mimeType = Android.Webkit.MimeTypeMap.Singleton.GetMimeTypeFromExtension(extension); 
    Intent intent = new Intent(Intent.ActionView); 
    intent.SetFlags(ActivityFlags.ClearTop | ActivityFlags.NewTask); 
    path = FileProvider.GetUriForFile(Android.App.Application.Context, Android.App.Application.Context.PackageName + ".provider", file); 
    intent.SetDataAndType(path, mimeType); 
    intent.AddFlags(ActivityFlags.GrantReadUriPermission); 
    Forms.Context.StartActivity(Intent.CreateChooser(intent, "Choose App")); 

Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments); is a specified system special folder and the folder should physically exist on your computer; otherwise, it returns an empty string (""). Please look into the following link to know more about this. 

When storage permission for App is not enabled in your android device,  
  1. myDir.Mkdir(); will be false and the folder Syncfusion will not be created in your device.
  2. That is when you face the No such file or directory exception.

Kindly try execute the shared sample in your device with storage permissions enabled for the App when it is created, and let us know if the file is saved in the device. 

Regards, 
Keerthi. 



BS Bhabani Sahoo July 29, 2020 07:13 AM UTC

Hi,
Thanks a lot for yoir time and patience. I have managed to resolve the problem. The file actually gets created eventhough I cant see it physically. When I tried to share/save the file in device from the same location, it does allow me to save successfully. That means the file is there but just not visible. I am sure it is the simulatir issue or something else but the file is definitely there.

Thank yoi once again for the support. Please close the case.
Kind regards


KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team July 29, 2020 07:30 AM UTC

Hi Bhabani, 

Thanks for the update. We are glad that you have found a solution. 

Regards, 
Keerthi. 



PV Pete Vickers October 5, 2020 01:44 PM UTC

Hi,
I have downloaded the sample and can't get it to work - I am using an Android 9 device.

If I leave the demo as it is then the line 

FileOutputStream outs = new FileOutputStream(file);

Fails as 'outs' is null.

If I force it to do

root = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);

then it succeeds in creating the file but chokes on the line

Intent intent = new Intent(Intent.ActionView);

If it succeeds then I can view it, but get a message saying the file doesn't exist. I need to allow the user to view the file AND email it

Thanks

Pete



KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team October 6, 2020 04:46 PM UTC

Hi Pete, 

Greeting from Syncfusion. 

We are checking your query and will update the details tomorrow (7th October 2020). 

Regards, 
Keerthi. 
 



KK Konduru Keerthi Konduru Ravichandra Raju Syncfusion Team October 7, 2020 08:36 AM UTC

Hi Pete, 

Greeting from Syncfusion. 

You need to enable storage permissions for the App your Android device, to overcome the file doesn't exist  issue and spot the file. Kindly try this and let us know if this helps. 

Regards, 
Keerthi. 



PV Pete Vickers October 7, 2020 08:58 AM UTC

Hi,
this is already set

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />

Thanks


PV Pete Vickers October 7, 2020 01:56 PM UTC

Hi,
I have found another way around it. Instead of trying to launch in the Android save, I just create the file, and then I can use Xamarin.Essentials to launch the program.

That works for me

Thanks



SK Shamini Kiruba Sobers Syncfusion Team October 8, 2020 11:27 AM UTC

Hi Pete, 

Thanks for the update. We are glad that you have found a solution. 
 
Regards, 
Shamini 


Loader.
Up arrow icon