Unable to attach file in WithAttachment in Xam.Plugins.Messaging - xamarin

please review my code as I am not able to attach any file in EmailMessageBuilder.
Also I need to understand about the ContentType, what should I pass in ContentType?
FileData filedata = await CrossFilePicker.Current.PickFile();
String Path = CrossGetLocalFilePath.Current.GetLocalPath(filedata.FileName);
var emailMessenger = CrossMessaging.Current.EmailMessenger;
if (emailMessenger.CanSendEmail)
{
var email = new EmailMessageBuilder()
.To("to.plugins#xamarin.com")
.Subject("Xamarin Messaging Plugin")
.Body("Well hello there from Xam.Messaging.Plugin")
.WithAttachment(Path, "image/jpeg")
.Build();
emailMessenger.SendEmail(email);
}
I am using above code in Xamarin.forms (Portable), my attachment could be an image, video or any file.
Getting error:
Failed to attach file due to IO error.

I never used the CrossFilePicker plugin and CrossGetLocalFilePath plugin before but I find the source code here:
CrossFilePicker : https://github.com/Studyxnet/FilePicker-Plugin-for-Xamarin-and-Windows/tree/master/FilePicker/FilePicker
CrossGetLocalFilePath:https://github.com/bradyjoslin/GetLocalFilePathPlugin/blob/master/GetLocalFilePath/GetLocalFilePath.Plugin.Android/GetLocalFilePathImplementation.cs
This is the FileData object you got when you call CrossFilePicker.Current.PickFile();
namespace Plugin.FilePicker.Abstractions
{
public class FileData
{
public byte[] DataArray { get; set; }
public string FileName { get; set; }
}
}
DataArray is your file data, and FileName is your file name. It does not contain the file path.
And you call the another plugin CrossGetLocalFilePath to get the file path according to the file name.
in the CrossGetLocalFilePath source code it just implements in Android platform:
public class GetLocalFilePathImplementation : IGetLocalFilePath
{
public string GetLocalPath(string fileName)
{
string path = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
return System.IO.Path.Combine(path, fileName);
}
}
it not return the file path you want. It returned system special file path.
So in your case it is not possible to get the file path by these plugins.
But I recommend you to overwrite the CrossFilePicker plugin.
Take UWP as an example:
public class FilePickerImplementation : IFilePicker
{
public async Task<FileData> PickFile()
{
var picker = new Windows.Storage.Pickers.FileOpenPicker();
picker.ViewMode = Windows.Storage.Pickers.PickerViewMode.List;
picker.SuggestedStartLocation =
Windows.Storage.Pickers.PickerLocationId.DocumentsLibrary;
picker.FileTypeFilter.Add("*");
Windows.Storage.StorageFile file = await picker.PickSingleFileAsync();
if (file != null)
{
var array = await ReadFile(file);
return new FileData
{
DataArray = array,
FileName = file.Name
FilePath = file.Path;
};
}
else
{
return null;
}
}
This is the implementation of file picker in UWP. You can add the FilePath property in the FileData Object as the code shows before.

We can get the path if we are using the below plugin for the Media Capture and Select image from the Gallery.
Xam.Plugin.Media
Thank You.

Related

Xamarin: Saving files to external storage on Android with an API level >=29

I'm trying to export files to the public external storage of an Android phone in Xamarin, for a backup DB. However, in the last version of Android phones (11.0 - API30), one can't opt-out of scoped storage using the property android:requestLegacyExternalStorage="true" of the <application> tag in the manifest.xml.
I made sure that the permissions READ_EXTERNAL_STORAGE & WRITE_EXTERNAL_STORAGE are granted before trying to create the file. Still, when trying to create a file, a System.UnauthorizedAccessException exception is thrown.
/* file 1: */
// ....
private async void Export_Tapped (object sender, EventArgs e) {
// check if permission for writing in external storage is given
bool canWrite = await FileSystem.ExternalStoragePermission_IsGranted ();
if (!canWrite) {
// request permission
canWrite = await FileSystem.ExternalStoragePermission_Request ();
if (!canWrite) {
// alert user
return;
}
}
// find the directory to export the db to, based on the platform
string fileName = "backup-" + DateTime.Now.ToString ("yyMMddThh:mm:ss") + ".db3";
string directory = FileSystem.GetDirectoryForDatabaseExport (); // returns "/storage/emulated/0/Download"
string fullPath = Path.Combine (directory, fileName);
// copy db to the directory
bool copied = false;
if (directory != null)
copied = DBConnection.CopyDatabase (fullPath);
if (copied)
// alert user
else
// alert user
}
// ....
/* file 2: */
class DBConnection
{
private readonly string dbPath;
// ....
public bool CopyDatabase(string path)
{
byte[] dbFile = File.ReadAllBytes(dbPath);
File.WriteAllBytes(path, dbFile); // --> this is where the exception is thrown <--
return true;
}
// ....
}
So the question stands: how does one write a new file to the public external storage of an Android device with an API level of 29 or more?
All the resources I have found so far, maybe you can gather more information than I did:
https://forums.xamarin.com/discussion/179999/access-denied-to-external-storage
(regarding private external storage) https://forums.xamarin.com/discussion/171039/saving-files-to-external-storage
https://learn.microsoft.com/en-us/xamarin/android/platform/files/external-storage?tabs=windows
https://developer.android.com/about/versions/11/privacy/storage#permissions
Try This , I use a dependency service to call this method in Native Android and save files like docx and pdf from their by byte array.
public async Task<bool> CreateFile(string fileName, byte[] docBytes, string fileType)
{
try
{
Java.IO.File file = new Java.IO.File(Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).AbsolutePath, fileName + fileType);
OutputStream os = new FileOutputStream(file);
os.Write(docBytes);
os.Close();
}
catch
{
return false;
}
return true;
}
The path you use is incorrect, please try the following file path .
Context context = Android.App.Application.Context;
var filePath = context.GetExternalFilesDir(Android.OS.Environment.DirectoryDocuments);
Refer to https://forums.xamarin.com/discussion/comment/422501/#Comment_422501 .

How to get Image from directory path in xamarin forms

I want to retrieve image which i have it's path in phone
filepath = "/storage/emulated/0/Android/data/com.CommunicatorEye/files/Pictures/EmployeesCards/IMG_20190131_143513.jpg";
var image = DependencyService.Get<IDependency().RetriveImageFromLocation(filepath);
IDependency.cs
public interface IDependency
{
Task<Image> RetriveImageFromLocation(string location);
}
Android
DependencyImplementation.cs
public async Task<Image> RetriveImageFromLocation(string location)
{
Image image = new Image();
var memoryStream = new MemoryStream();
using (var source = System.IO.File.OpenRead(location))
{
await source.CopyToAsync(memoryStream);
}
image.Source = ImageSource.FromStream(() => memoryStream);
return image;
}
but it doesn't work for me , any sample ?
If that file is within your app's sandbox, there is no reason to use DI/DependencyService/etc... to obtain a stream to populate an ImageSource and then add that to an Image.
Use an FileImageSource (static ImageSource.FromFile) and supply it the path:
var image = new Image
{
Source = ImageSource.FromFile(filePath)
};
This is how you can get path of resources. keys should be declared in App.xaml file
public static String GetImagePath(string AppResourceName)
{
return (Application.Current.Resources[AppResourceName] as FileImageSource).File;
}
public static Color GetColor(string AppResourceName)
{
return (Color)Application.Current.Resources[AppResourceName];
}

store voice file path android xamarin

the following code is what I use to grab a voice file to send an email as an attachment. However, I am not able to find this file. no clue where it is stored. (this is what I see when I hover the path /storage/emulated/0/test.mp4). the storage folder is empty even when this run, for this reason, the attachment can't be sent. any ideas? Thank you! updated code
string path = "";
public Recorder_Droid()
{
var sqlliteFilname = "test.mp4";
string filePath = global::Android.OS.Environment.ExternalStorageDirectory.AbsolutePath;
path = Path.Combine(filePath, sqlliteFilname);
_recorder = new MediaRecorder();
_player = new MediaPlayer();
_player.Completion += (sender, e) => {
_player.Reset();
};
}
MediaRecorder _recorder;
MediaPlayer _player;
public void PlayAudio()
{
if (File.Exists(path))
{
File.Delete(path);
}
if (_recorder == null)
{
_recorder = new MediaRecorder();
}
_recorder.Reset();
_recorder.SetAudioSource(AudioSource.Mic);
_recorder.SetOutputFormat(OutputFormat.Mpeg4);
_recorder.SetAudioEncoder(AudioEncoder.Aac);
_recorder.SetOutputFile(path);
_recorder.Prepare(); // Prepared state
_recorder.Start(); // Recording state.
return;

How to download a ZipFile from a dotnet core webapi?

I am trying to download a zip file from a dotnet core web api action, but I can't make it work. I tried calling the action via POSTMAN and my Aurelia Http Fetch Client.
I am able to create the ZipFile like I want it and store it on the system, but can't fix it so it returns the zipfile via the api.
Use-case: User selects a couple of picture collections and clicks the download button. The ids of the picture collections gets send to the api and a zipfile is created which contains a directory for every picture collection which holds the pictures. That zipfile is returned to the user so he/she can store it on their system.
Any help would be appreciated.
My controller action
/// <summary>
/// Downloads a collection of picture collections and their pictures
/// </summary>
/// <param name="ids">The ids of the collections to download</param>
/// <returns></returns>
[HttpPost("download")]
[ProducesResponseType(typeof(void), (int) HttpStatusCode.OK)]
public async Task<IActionResult> Download([FromBody] IEnumerable<int> ids)
{
// Create new zipfile
var zipFile = $"{_ApiSettings.Pictures.AbsolutePath}/collections_download_{Guid.NewGuid().ToString("N").Substring(0,5)}.zip";
using (var repo = new PictureCollectionsRepository())
using (var picturesRepo = new PicturesRepository())
using (var archive = ZipFile.Open(zipFile, ZipArchiveMode.Create))
{
foreach (var id in ids)
{
// Fetch collection and pictures
var collection = await repo.Get(id);
var pictures = await picturesRepo
.GetAll()
.Where(x => x.CollectionId == collection.Id)
.ToListAsync();
// Create collection directory IMPORTANT: the trailing slash
var directory = $"{collection.Number}_{collection.Name}_{collection.Date:yyyy-MM-dd}/";
archive.CreateEntry(directory);
// Add the pictures to the current collection directory
pictures.ForEach(x => archive.CreateEntryFromFile(x.FilePath, $"{directory}/{x.FileName}"));
}
}
// What to do here so it returns the just created zip file?
}
}
My aurelia fetch client function:
/**
* Downloads all pictures from the picture collections in the ids array
* #params ids The ids of the picture collections to download
*/
download(ids: Array<number>): Promise<any> {
return this.http.fetch(AppConfiguration.baseUrl + this.controller + 'download', {
method: 'POST',
body: json(ids)
})
}
What I've tried
Note that what I've tried does not generate errors, it just doesn't seems to do anything.
1) Creating my own FileResult (like I used to do with older ASP.NET). Can't see the headers being used at all when I call it via postman or the application.
return new FileResult(zipFile, Path.GetFileName(zipFile), "application/zip");
public class FileResult : IActionResult
{
private readonly string _filePath;
private readonly string _contentType;
private readonly string _fileName;
public FileResult(string filePath, string fileName = "", string contentType = null)
{
if (filePath == null) throw new ArgumentNullException(nameof(filePath));
_filePath = filePath;
_contentType = contentType;
_fileName = fileName;
}
public Task ExecuteResultAsync(ActionContext context)
{
var response = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(System.IO.File.ReadAllBytes(_filePath))
};
if (!string.IsNullOrEmpty(_fileName))
response.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = _fileName
};
response.Content.Headers.ContentType = new MediaTypeHeaderValue(_contentType);
return Task.FromResult(response);
}
}
}
2) https://stackoverflow.com/a/34857134/2477872
Does nothing.
HttpContext.Response.ContentType = "application/zip";
var result = new FileContentResult(System.IO.File.ReadAllBytes(zipFile), "application/zip")
{
FileDownloadName = Path.GetFileName(zipFile)
};
return result;
I've tried it with a test dummy PDF file and that seemed to work with POSTMAN. But when I try to change it to the zipfile (see above) it does nothing.
HttpContext.Response.ContentType = "application/pdf";
var result = new FileContentResult(System.IO.File.ReadAllBytes("THE PATH/test.pdf"), "application/pdf")
{
FileDownloadName = "test.pdf"
};
return result;
To put a long story short, the example below illustrates how to easily serve both a PDF as well as a ZIP through a dotnet-core api:
/// <summary>
/// Serves a file as PDF.
/// </summary>
[HttpGet, Route("{filename}/pdf", Name = "GetPdfFile")]
public IActionResult GetPdfFile(string filename)
{
const string contentType = "application/pdf";
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(#"{path_to_files}\file.pdf"), contentType)
{
FileDownloadName = $"{filename}.pdf"
};
return result;
}
/// <summary>
/// Serves a file as ZIP.
/// </summary>
[HttpGet, Route("{filename}/zip", Name = "GetZipFile")]
public IActionResult GetZipFile(string filename)
{
const string contentType ="application/zip";
HttpContext.Response.ContentType = contentType;
var result = new FileContentResult(System.IO.File.ReadAllBytes(#"{path_to_files}\file.zip"), contentType)
{
FileDownloadName = $"{filename}.zip"
};
return result;
}
This sample just works™
Notice in this case there is only one main difference between the two actions (besied the source file name, of course): the contentType that is returned.
The example above uses 'application/zip', as you've mentioned yourself, but it might just be required to serve a different mimetype (like 'application/octet*').
This leads to the speculation that either the zipfile cannot be read properly or that your webserver configuration might not be configured properly for serving .zip files.
The latter may differ based on whether you're running IIS Express, IIS, kestrel etc. But to put this to the test, you could try adding a zipfile to your ~/wwwroot folder, making sure you have enabled serving static files in your Status.cs, to see if you can download the file directly.

Error in file download using ASP MVC3

This code is supposed to download a file using mvc3 controller
public FilePathResult GetFileFromDisk(String file)
{
String path = AppDomain.CurrentDomain.BaseDirectory + "AppData/";
String contentType = "text/plain";
return File(path+file, contentType, file);
}
View part :
#Html.ActionLink("Download", "GetFileFromDisk","Upload", new { file = "textfile" },null);
But when i click the link I am getting this error
Could not find a part of the path 'D:\Project\FileUploadDownload\FileUploadDownload\AppData\textfile'.
[DirectoryNotFoundException: Could not find a part of the path 'D:\Project\FileUploadDownload\FileUploadDownload\AppData\textfile'.]
Why the foldername is repeating in the file path? Please offer a solution...
Try like this:
public ActionResult GetFileFromDisk(string file)
{
var appData = Server.MapPath("~/App_Data");
var path = Path.Combine(appData, file);
path = Path.GetFullPath(path);
if (!path.StartsWith(appData))
{
// Ensure that we are serving file only inside the App_Data folder
// and block requests outside like "../web.config"
throw new HttpException(403, "Forbidden");
}
if (!System.IO.File.Exists(path))
{
return HttpNotFound();
}
var contentType = "text/plain";
return File(path, contentType, Path.GetFileName(path));
}

Resources