i try to open a existing PDF file on a iOS device.
This file have to be open with the default PDF reader.
In this moment i use the "dependency service" to run native code.
public void Save(string filename, byte[] byPDF)
{
string strPfad = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), filename);
if(File.Exists(strPfad))
{
File.Delete(strPfad);
File.WriteAllBytes(strPfad, byPDF);
}
else
File.WriteAllBytes(strPfad, byPDF);
var viewer = UIDocumentInteractionController.FromUrl(NSUrl.FromFilename(strPfad));
var controller = GetVisibleViewController();
viewer.PresentOpenInMenu(controller.View.Frame, controller.View, true);
}
private UIViewController GetVisibleViewController(UIViewController controller = null)
{
controller = controller ?? UIApplication.SharedApplication.KeyWindow.RootViewController;
if (controller.PresentedViewController == null)
return controller;
if (controller.PresentedViewController is UINavigationController)
{
return ((UINavigationController)controller.PresentedViewController).VisibleViewController;
}
if (controller.PresentedViewController is UITabBarController)
{
return ((UITabBarController)controller.PresentedViewController).SelectedViewController;
}
return GetVisibleViewController(controller.PresentedViewController);
}
If I run this code is nothing happend (only the file becomes written).
I just used a standard UIViewController and passed the path (where the pdf is saved on the device) to the controller and loaded it up in a UIWebview.
public class PdfController : UIViewController
{
public PdfController(string pdfPath)
{
NavigationItem.LeftBarButtonItem = new NavBarButton("Back", (sender, args) =>
{
NavigationController.PopViewController(true);
});
var webView = new UIWebView(View.Bounds);
View.AddSubview(webView);
webView.LoadRequest(new NSUrlRequest(new NSUrl(pdfPath, false)));
webView.ScalesPageToFit = true;
}
}
But you will need to download it first and pass it to this controller
This snippit will allow you download the pdf and save it.
Public void DownloadPDF()
{
Utility.AddNetworkConnection();
var webClient = new WebClient();
loadingView = new LoadingView();
loadingView.Show("Downloading PDF");
webClient.DownloadDataCompleted += (s, e) =>
{
Utility.RemoveNetworkConnection();
File.WriteAllBytes(_pdfPathLocation, e.Result); // writes to local storage
InvokeOnMainThread(() =>
{
loadingView.Hide();
_pdfImageElement.SetValueAndUpdate("Open PDF");
var a = new UIAlertView("Done", "File downloaded and saved", null, "OK", "Open PDF");
a.Show();
a.Clicked += OpenPdf;
});
};
var url = new Uri(_wreck.PdfURL);
webClient.Encoding = Encoding.UTF8;
webClient.DownloadDataAsync(url);
}
Related
I'm trying to play video with AvPlayer in Xamarin.iOS. I'm working with Firebase Storage. If I upload video from android, media type is set as mp4 but from iOS, media type is set as urlencoded. I can play mp4 files with AvPlayer but urlencoded files are not playable. On the other hand, urlencoded files are playable in android VideoView. Do you have any idea about it?
Here my codes, first pick video from gallery:
private async void PickVideoButton_TouchUpInside(object sender, EventArgs e)
{
await CrossMedia.Current.Initialize();
if (!CrossMedia.Current.IsPickVideoSupported)
{
return;
}
try
{
file = await CrossMedia.Current.PickVideoAsync();
if (file == null)
{
return;
}
fileStream = await ConvertMovToMp4();
mediaType = "Video";
avp = new AVPlayer(NSUrl.FromFilename(file.Path));
avpvc = new AVPlayerViewController();
avpvc.Player = avp;
AddChildViewController(avpvc);
GeneralPostingStoryViewBackground.AddSubview(avpvc.View);
avpvc.View.Frame = GeneralPostingStoryImageView.Frame;
avpvc.ShowsPlaybackControls = true;
avp.Play();
}
catch (Exception ex)
{
alert = UIAlertController.Create("Error", "Gallery doesn't support", UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
PresentViewController(alert, true, null);
}
}
public async Task<Stream> ConvertMovToMp4()
{
string exportPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
string exportFilePath = Path.Combine(exportPath, DateTime.Now.ToString() + ".mp4");
var asset = AVAsset.FromUrl(NSUrl.FromFilename(file.Path));
var length = asset.Duration.Seconds;
lengthDuration = Convert.ToInt32(length).ToString();
AVAssetExportSession export = new AVAssetExportSession(asset, AVAssetExportSession.PresetMediumQuality);
export.OutputUrl = NSUrl.FromFilename(exportFilePath);
export.OutputFileType = AVFileType.Mpeg4;
export.ShouldOptimizeForNetworkUse = true;
await export.ExportTaskAsync();
var stream = File.OpenRead(exportFilePath);
return stream;
}
Then, upload video to firebase storage:
private async void ShareButton_TouchUpInside(object sender, EventArgs e)
{
try
{
var result = await PortableSharediOS(ID, mediaType, fileStream, commentText);
if (result == "Success.")
{
CommonValues.viewControllerIndexList.RemoveAt(CommonValues.viewControllerIndexList.Count - 1);
NavigateViewController();
}
else
{
alert = UIAlertController.Create("Error", result.ToString(), UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
PresentViewController(alert, true, null);
}
}
catch (Exception ex)
{
alert = UIAlertController.Create("Error", "Check your internet connection.", UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
PresentViewController(alert, true, null);
}
}
Finally, I'm trying to play videos, as I said the mp4 files are playable which I've upload from android, but files which I've upload from iOS, avplayer doesn't play them..:
public async Task GetStory(int storyIndex)
{
var mediaType = stories[storyIndex].StoryType;
var story = stories[storyIndex];
user = await firebaseHelper.GetUser(story.StoryOwner);
if (mediaType == "Photo")
{
GetImage(story.MediaLink, storyViewStoryImageView);
GetImage(user.PhotoLink, storyViewImageView);
storyViewUserName.Text = user.UserName;
storyViewContentView.Text = story.Content;
time = story.MediaDuration;
storyViewDuration.Text = time.ToString();
}
else
{
storyViewUserName.Text = user.UserName;
storyViewContentView.Text = story.Content;
time = story.MediaDuration;
var asset = AVAsset.FromUrl(NSUrl.FromString(story.MediaLink));
var item = AVPlayerItem.FromAsset(asset);
avp = new AVPlayer(item);
avp.Muted = false;
avpvc = new AVPlayerViewController();
avpvc.Player = avp;
AddChildViewController(avpvc);
storyViewStoryImageView.AddSubview(avpvc.View);
avpvc.View.Hidden = false;
avpvc.View.Frame = storyViewStoryImageView.Frame;
avpvc.ShowsPlaybackControls = false;
avp.Play();
storyViewDuration.Text = time.ToString();
}
timer.Enabled = false;
timer.Close();
TimerTextVoid();
}
I can play all files on Android. Doesn't matter where they were uploaded from.
I figured out my problem. Download and save file to device from Firebase Storage. Then assign it to AVAsset. Codes:
public async Task GetStory(int storyIndex)
{
var mediaType = stories[storyIndex].StoryType;
var story = stories[storyIndex];
user = await firebaseHelper.GetUser(story.StoryOwner);
if (mediaType == "Photo")
{
GetImage(story.MediaLink, storyViewStoryImageView);
GetImage(user.PhotoLink, storyViewImageView);
storyViewUserName.Text = user.UserName;
storyViewContentView.Text = story.Content;
time = story.MediaDuration;
storyViewDuration.Text = time.ToString();
}
else
{
storyViewUserName.Text = user.UserName;
storyViewContentView.Text = story.Content;
time = story.MediaDuration;
var asset = await GetVideo(story.MediaLink);
var item = new AVPlayerItem(asset);
avp = new AVPlayer(item);
avp.Muted = false;
avpvc = new AVPlayerViewController();
avpvc.Player = avp;
AddChildViewController(avpvc);
storyViewStoryImageView.AddSubview(avpvc.View);
avpvc.View.Hidden = false;
avpvc.View.Frame = storyViewStoryImageView.Frame;
avpvc.ShowsPlaybackControls = false;
avp.Play();
storyViewDuration.Text = time.ToString();
}
timer.Enabled = false;
timer.Close();
TimerTextVoid();
}
public async Task<AVAsset> GetVideo(string url)
{
string videoFile;
using (var client = new WebClient())
{
var content = client.DownloadData(url);
var stream = new MemoryStream(content);
string folder = Path.GetTempPath();
videoFile = Path.Combine(folder, DateTime.Now.ToString() + ".mp4");
if (!System.IO.File.Exists(videoFile))
{
using (FileStream outputStream = System.IO.File.Create(videoFile))
{
await stream.CopyToAsync(outputStream);
}
}
}
var asset = AVUrlAsset.FromUrl(NSUrl.FromFilename(videoFile));
return asset;
}
It would appear that something has changed in recent versions of iOS as it relates to saving GPS data in photos and subsequent to a bug in the Xam.Plugin.Media library, I am attempting to figure out how to take a photo and save it to the Photo Gallery with intact GPS data using iOS 13.4.1 and Xamarin.iOS 13.16.0.13. I have looked at the internals of Xam.Plugin.Media and using this as a starting point, tried to cobble something together on the hunch that because Xam.Plugin.Media uses ALAssetsLibrary to save to the Photo Gallery, perhaps this was why the GPS data is not being saved with the photo.
I am currently at the point where I had thought I would be able to take the photo, merge GPS data into the file and save the JPG output to a temporary location in the app folder (i.e. I haven't even gotten to saving the photo to the gallery yet). But when I examine this temp file using either the Preview app on MacOS or Photoshop to view the GPS metadata, the data is not there.
My handler for UIImagePickerController.FinishedPickingMedia is this:
private void OnFinishedPickingMedia(object sender, UIImagePickerMediaPickedEventArgs e)
{
try
{
NSDictionary info = e.Info;
NSDictionary meta = null;
UIImage image = null;
string savedImagePath = null;
if (_imagePicker.SourceType == UIImagePickerControllerSourceType.Camera)
{
meta = (NSDictionary)info[UIImagePickerController.MediaMetadata];
image = (UIImage)info[UIImagePickerController.OriginalImage];
if (meta != null && meta.ContainsKey(ImageIO.CGImageProperties.Orientation))
{
var newMeta = new NSMutableDictionary();
newMeta.SetValuesForKeysWithDictionary(meta);
var newTiffDict = new NSMutableDictionary();
newTiffDict.SetValuesForKeysWithDictionary(meta[ImageIO.CGImageProperties.TIFFDictionary] as NSDictionary);
newTiffDict.SetValueForKey(meta[ImageIO.CGImageProperties.Orientation], ImageIO.CGImageProperties.TIFFOrientation);
newMeta[ImageIO.CGImageProperties.TIFFDictionary] = newTiffDict;
meta = newMeta;
}
if (_locationPermissionGranted)
{
meta = SetGpsLocation(meta);
}
savedImagePath = SaveImageWithMetadata(image, meta);
}
string aPath = null;
if (_imagePicker.SourceType != UIImagePickerControllerSourceType.Camera)
{
// Try to get the album path's URL.
var url = (NSUrl)info[UIImagePickerController.ReferenceUrl];
aPath = url?.AbsoluteString;
}
}
catch (Exception ex)
{
Console.WriteLine($"Unable to get metadata: {ex}");
}
}
Which calls:
private NSDictionary SetGpsLocation(NSDictionary meta)
{
var newMeta = new NSMutableDictionary();
newMeta.SetValuesForKeysWithDictionary(meta);
var newGpsDict = new NSMutableDictionary();
newGpsDict.SetValueForKey(new NSNumber(Math.Abs(_lat)), ImageIO.CGImageProperties.GPSLatitude);
newGpsDict.SetValueForKey(new NSString(_lat > 0 ? "N" : "S"), ImageIO.CGImageProperties.GPSLatitudeRef);
newGpsDict.SetValueForKey(new NSNumber(Math.Abs(_long)), ImageIO.CGImageProperties.GPSLongitude);
newGpsDict.SetValueForKey(new NSString(_long > 0 ? "E" : "W"), ImageIO.CGImageProperties.GPSLongitudeRef);
newGpsDict.SetValueForKey(new NSNumber(_altitude), ImageIO.CGImageProperties.GPSAltitude);
newGpsDict.SetValueForKey(new NSNumber(0), ImageIO.CGImageProperties.GPSAltitudeRef);
newGpsDict.SetValueForKey(new NSNumber(_speed), ImageIO.CGImageProperties.GPSSpeed);
newGpsDict.SetValueForKey(new NSString("K"), ImageIO.CGImageProperties.GPSSpeedRef);
newGpsDict.SetValueForKey(new NSNumber(_direction), ImageIO.CGImageProperties.GPSImgDirection);
newGpsDict.SetValueForKey(new NSString("T"), ImageIO.CGImageProperties.GPSImgDirectionRef);
newGpsDict.SetValueForKey(new NSString(_timestamp.ToString("hh:mm:ss")), ImageIO.CGImageProperties.GPSTimeStamp);
newGpsDict.SetValueForKey(new NSString(_timestamp.ToString("yyyy:MM:dd")), ImageIO.CGImageProperties.GPSDateStamp);
newMeta[ImageIO.CGImageProperties.GPSDictionary] = newGpsDict;
return newMeta;
}
private string SaveImageWithMetadata(UIImage image, NSDictionary meta)
{
string outputPath = null;
try
{
var finalQuality = 1.0f;
var imageData = image.AsJPEG(finalQuality);
// Continue to move down quality, rare instances.
while (imageData == null && finalQuality > 0)
{
finalQuality -= 0.05f;
imageData = image.AsJPEG(finalQuality);
}
if (imageData == null)
{
throw new NullReferenceException("Unable to convert image to jpeg, please ensure file exists or " +
"lower quality level");
}
var dataProvider = new CGDataProvider(imageData);
var cgImageFromJpeg = CGImage.FromJPEG(dataProvider, null, false, CGColorRenderingIntent.Default);
var imageWithExif = new NSMutableData();
var destination = CGImageDestination.Create(imageWithExif, UTType.JPEG, 1);
var cgImageMetadata = new CGMutableImageMetadata();
var destinationOptions = new CGImageDestinationOptions();
if (meta.ContainsKey(ImageIO.CGImageProperties.Orientation))
{
destinationOptions.Dictionary[ImageIO.CGImageProperties.Orientation] =
meta[ImageIO.CGImageProperties.Orientation];
}
if (meta.ContainsKey(ImageIO.CGImageProperties.DPIWidth))
{
destinationOptions.Dictionary[ImageIO.CGImageProperties.DPIWidth] =
meta[ImageIO.CGImageProperties.DPIWidth];
}
if (meta.ContainsKey(ImageIO.CGImageProperties.DPIHeight))
{
destinationOptions.Dictionary[ImageIO.CGImageProperties.DPIHeight] =
meta[ImageIO.CGImageProperties.DPIHeight];
}
if (meta.ContainsKey(ImageIO.CGImageProperties.ExifDictionary))
{
destinationOptions.ExifDictionary =
new CGImagePropertiesExif(meta[ImageIO.CGImageProperties.ExifDictionary] as NSDictionary);
}
if (meta.ContainsKey(ImageIO.CGImageProperties.TIFFDictionary))
{
destinationOptions.TiffDictionary =
new CGImagePropertiesTiff(meta[ImageIO.CGImageProperties.TIFFDictionary] as NSDictionary);
}
if (meta.ContainsKey(ImageIO.CGImageProperties.GPSDictionary))
{
destinationOptions.GpsDictionary =
new CGImagePropertiesGps(meta[ImageIO.CGImageProperties.GPSDictionary] as NSDictionary);
}
if (meta.ContainsKey(ImageIO.CGImageProperties.JFIFDictionary))
{
destinationOptions.JfifDictionary =
new CGImagePropertiesJfif(meta[ImageIO.CGImageProperties.JFIFDictionary] as NSDictionary);
}
if (meta.ContainsKey(ImageIO.CGImageProperties.IPTCDictionary))
{
destinationOptions.IptcDictionary =
new CGImagePropertiesIptc(meta[ImageIO.CGImageProperties.IPTCDictionary] as NSDictionary);
}
destination.AddImageAndMetadata(cgImageFromJpeg, cgImageMetadata, destinationOptions);
var success = destination.Close();
if (success)
{
outputPath = GetOutputPath();
imageWithExif.Save(outputPath, true);
}
}
catch (Exception e)
{
Console.WriteLine($"Unable to save image with metadata: {e}");
}
return outputPath;
}
private static string GetOutputPath()
{
var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "temp");
Directory.CreateDirectory(path);
var timestamp = DateTime.UtcNow.ToString("yyyMMdd_HHmmss", CultureInfo.InvariantCulture);
var name = "IMG_" + timestamp + ".jpg";
return Path.Combine(path, GetUniquePath(path, name));
}
private static string GetUniquePath(string path, string name)
{
var ext = Path.GetExtension(name);
name = Path.GetFileNameWithoutExtension(name);
var fullName = name + ext;
var i = 1;
while (File.Exists(Path.Combine(path, fullName)))
{
fullName = name + "_" + (i++) + ext;
}
return Path.Combine(path, fullName);
}
The file is saved successfully, but without the expected GPS metadata. Which leads me to believe that the problem may be to do with the way I am saving the temporary photo or saving the GPS metadata to it in either SaveImageWithMetadata or SetGpsLocation.
If anyone can provide some info on what actually works now with iOS 13.4.1 and saving GPS data to photos, I would be very appreciative.
I have an Xamarin.Forms-based app which runs on Android and iOS. Right now, I am implementing the feature of selecting images from the camera roll and uploading it to our server. Therefore, I am writing platform-specific code for iOS, which is where the error occurs.
I am calling the UIImagePickerController from a platform-specific renderer for iOS. It opens normally. But when tapping on an image in the UIImagePickerController nothing happens, except Visual Studio showing a message in the debug console:
"Warning: Attempt to present Xamarin_Forms_Platform_iOS_ModalWrapper: 0x155a7ed00 on Xamarin_Forms_Platform_iOS_PlatformRenderer: 0x153ead6a0 whose view is not in the window hierarchy!"
I googled and found somebody writing a function called "GetVisibleViewController" which i adapted to my project (you can see it below). On the ViewController which that function returns, I call the PresentModalViewController() method. Unfortunately, it is not working either. It is not possible to select a photo.
private void ChoosePhoto()
{
_imagePicker = new UIImagePickerController()
{
SourceType = UIImagePickerControllerSourceType.PhotoLibrary,
MediaTypes = new string[] { UTType.Image }
};
_imagePicker.FinishedPickingMedia += delegate (object sender, UIImagePickerMediaPickedEventArgs e)
{
var fileName = eopAppLibrary.Tools.GetTimestampJpegFileName("ScanToEop_iOS");
var jpegImageData = e.OriginalImage.AsJPEG();
var jpegBytes = jpegImageData.ToArray();
Events.RaiseFilePreviewNeeded(this, jpegBytes, fileName);
};
_imagePicker.Canceled += delegate (object sender, EventArgs e)
{
_imagePicker.DismissModalViewController(true);
};
var viewController = GetVisibleViewController();
viewController.PresentModalViewController(_imagePicker, true);
}
UIViewController GetVisibleViewController(UIViewController controller = null)
{
controller = controller ?? UIApplication.SharedApplication.KeyWindow.RootViewController;
if (controller.PresentedViewController == null)
{
return controller;
}
if (controller.PresentedViewController is UINavigationController)
{
return ((UINavigationController)controller.PresentedViewController).VisibleViewController;
}
if (controller.PresentedViewController is UITabBarController)
{
return ((UITabBarController)controller.PresentedViewController).SelectedViewController;
}
return GetVisibleViewController(controller.PresentedViewController);
}
We had a similar issue and here is what we came up with:
var topViewController = UIApplication.SharedApplication.KeyWindow.RootViewController;
var controllerToPresentWith = topViewController.VisibleViewController();
controllerToPresentWith.PresentModalViewController(_imagePicker, true);
and then
...
public static UIViewController VisibleViewController(this UIViewController controller)
{
if (controller == null)
return null;
if (controller is UINavigationController navController)
{
return navController.VisibleViewController();
}
else if (controller is UITabBarController tabController)
{
tabController.SelectedViewController?.VisibleViewController();
}
else
{
var vc = controller.PresentedViewController?.VisibleViewController();
if (vc != null)
return vc;
}
return controller;
}
In the end, I implemented this by using James Montemagno's Media Plugin library (available over NuGet: https://www.nuget.org/packages/Xam.Plugin.Media) and Permissions Plugin (https://www.nuget.org/packages/Plugin.Permissions).
I wrote the following code for this:
private async Task ChoosePhoto()
{
var permission = await CheckCameraRollPermission();
if (permission == PermissionStatus.Granted)
{
await CrossMedia.Current.Initialize();
// Show image picker dialog
var file = await CrossMedia.Current.PickPhotoAsync(new Plugin.Media.Abstractions.PickMediaOptions()
{
ModalPresentationStyle = Plugin.Media.Abstractions.MediaPickerModalPresentationStyle.OverFullScreen
});
if (file != null)
{
// Image has been selected
using (var stream = file.GetStream())
{
using (var memoryStream = new System.IO.MemoryStream())
{
stream.CopyTo(memoryStream);
var fileBytes = memoryStream.ToArray();
// DO WHATEVER YOU WANT TO DO WITH THE SELECTED IMAGE AT THIS POINT
}
}
}
}
}
private async Task<PermissionStatus> CheckCameraRollPermission()
{
// Check permission for image library access
var permission = await PermissionsImplementation.Current.CheckPermissionStatusAsync(Permission.Photos);
if (permission != PermissionStatus.Granted)
{
// Permission has not been granted -> if permission has been requested before and the user did not grant it, show message and return the permission
var message = "";
switch (permission)
{
case PermissionStatus.Denied:
case PermissionStatus.Restricted:
message = "Unfortunately, you did not grant permission to access the camera roll. If you want to change this, you can do so in the system settings of your device.";
break;
default:
break;
}
if (!string.IsNullOrEmpty(message))
{
// Message available -> Display alert and return the permission
var alert = UIAlertController.Create("Permission not granted", message, UIAlertControllerStyle.Alert);
alert.AddAction(UIAlertAction.Create("OK", UIAlertActionStyle.Default, null));
PresentViewController(alert, true, null);
return permission;
}
// In all other cases, request the permission
await PermissionsImplementation.Current.RequestPermissionsAsync(Permission.Photos);
// Check for permission one more time and return it
permission = await PermissionsImplementation.Current.CheckPermissionStatusAsync(Permission.Photos);
}
return permission;
}
I am accessing to an Enterprise Intranet using a WebView, in a Xamarin Android app. I can see and navigate correctly through the intranet but I am not able to download the files available there. This is my code :
private void MWebview_Download(object sender, DownloadEventArgs e)
{
var url = e.Url;
// var s = url.Replace(" ", "%20");
DownloadManager.Request request = new DownloadManager.Request(Android.Net.Uri.Parse(url));
string credentials = "cristina.casas:Tst.30"; //just for try
// pasar las credenciales a base64
var plainTextBytes = System.Text.Encoding.UTF8.GetBytes(credentials);
var encodedCredentials = System.Convert.ToBase64String(plainTextBytes);
request.AddRequestHeader("Authorization", "Basic " + encodedCredentials);
request.SetTitle("descarga.pdf");
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
request.AllowScanningByMediaScanner();
request.SetMimeType("application/pdf");
request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, "descarga.pdf");
DownloadManager dm = (DownloadManager)Application.Context.GetSystemService(Context.DownloadService);
dm.Enqueue(request);
Toast.MakeText(Application.Context, "Downloading File", ToastLength.Long).Show();//To notify the Client that the file is being downloaded
}
It doesn't work. I get the error "download failed". I am stucked at this point for days...
Your code looks correct. Try the following as this works as a basic authentication test using HttpWatch's website. If it works for you, substitute your intranet's uri, user and password.
DownloadCompleteReceiver receiver;
var user = "httpwatch";
var password = new Random().Next(int.MinValue, int.MaxValue).ToString();
var uriString = "https://www.httpwatch.com/httpgallery/authentication/authenticatedimage/default.aspx?0.05205263447822417";
using (var uri = Android.Net.Uri.Parse(uriString))
using (var request = new DownloadManager.Request(uri))
{
var basicAuthentication = Convert.ToBase64String(Encoding.UTF8.GetBytes($"{user}:{password}"));
request.AddRequestHeader("Authorization", $"Basic {basicAuthentication}");
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
request.SetDestinationInExternalPublicDir(Android.OS.Environment.DirectoryDownloads, "someImage.gif");
using (var downloadManager = (DownloadManager)GetSystemService(DownloadService))
{
var id = downloadManager.Enqueue(request);
receiver = new DownloadCompleteReceiver(id, (sender, e) =>
{
Toast.MakeText(Application.Context, $"Download Complete {id}", ToastLength.Long).Show();
if (sender is DownloadCompleteReceiver rec)
{
UnregisterReceiver(rec);
rec.Dispose();
}
});
RegisterReceiver(receiver, new IntentFilter(DownloadManager.ActionDownloadComplete));
Toast.MakeText(Application.Context, $"Downloading File: {id}", ToastLength.Short).Show();
}
}
The DownloadCompleteReceiver implementation is:
public class DownloadCompleteReceiver : BroadcastReceiver
{
long id;
EventHandler handler;
public DownloadCompleteReceiver(long id, EventHandler handler)
{
this.id = id;
this.handler = handler;
}
public override void OnReceive(Context context, Intent intent)
{
if (intent.Action == DownloadManager.ActionDownloadComplete &&
id == intent.GetLongExtra(DownloadManager.ExtraDownloadId, 0))
{
handler.Invoke(this, EventArgs.Empty);
}
}
}
I'm having an issue in downloading a file in my Angular project. The problem is that when I try to navigate to the file's URL, the file does download successfully. But how can I implement the downloading function in Angular?
[VRoute("PassportAttachments/{id}", 1)]
[HttpGet]
[AllowAnonymous]
public HttpResponseMessage GetPassportAttachmentById(int individualId, [FromUri] int id = -1)
{
try
{
var attachment = _passportAttachmentManager.FindById(id);
string attachmentPath = HttpContext.Current.Server.MapPath(
string.Format(ConfigurationManager.AppSettings["IndividualPassportsPath"], individualId.ToString()) + attachment.FileName);
//string downloadUrl = Url.Content(attachmentPath).Replace("/Api/Contacts/PassportAttachments/~", "");
//var result = new { DownloadUrl = downloadUrl, AttachmentTitle = attachment.Title };
//return Ok(result);
if (File.Exists(attachmentPath))
return new FileContentResult(attachmentPath, attachment.Title, FileResultType.ImageContentResult);
else
return null;
}
catch (Exception ex)
{
Unit.Logger.Error(ex, ToString(), ActionContext.ActionArguments.ToList());
return null;
//return NotFound();
}
}
FileContentResult constructor:
public FileContentResult(string FilePath, string ResponeFileName, FileResultType fileResultType) : base(HttpStatusCode.OK)
{
var stream = new FileStream(FilePath, FileMode.Open, FileAccess.Read);
base.Content = new StreamContent(stream);
base.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachments") { FileName = ResponeFileName };
switch (fileResultType)
{
case FileResultType.ZipContentResult:
base.Content.Headers.ContentType = new MediaTypeHeaderValue("application/zip");
break;
case FileResultType.ExcelContentResult:
base.Content.Headers.ContentType = new MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
break;
case FileResultType.PDFContentResult:
base.Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
break;
case FileResultType.ImageContentResult:
base.Content.Headers.ContentType = new MediaTypeHeaderValue("image/png");
break;
}
}
Now like I said, when I type the URL which downloads the file by myself (hence the AllowAnonymous) everything works fine. But function should I use or write to download the file using TypeScript
public DownloadAttachments(): void {
if (this.SelectedPassportAttachments != null && this.SelectedPassportAttachments.length > 0) {
if (this.SelectedPassportAttachments.length == 1) {
this.service.DownloadSinglePassportAttachment(this.SelectedPassportAttachments[0].Id, this.IndividualId).subscribe((file: any) => {
// download file (function)
});
}
else {
this.service.DownloadMultiplePassportAttachment(this.IndividualId, this.SelectedPassportAttachments.map(pa => pa.Id), this.SelectedPassportNumber).subscribe();
}
}
}
Since you are using a Content-Disposition header, the browser will automatically trigger a download dialog when it attempts to load the URL.
So you can either just navigate to the download location, or open the download location in a separate window (which will automatically close in most browsers when the download dialog appears):
// navigate to the URL:
window.location.href = downloadUrl;
// or open a new window
window.open(downloadUrl);
Note that opening a window will be blocked by popup blockers if you run window.open outside from mouse events (for example button clicks). You can avoid that by opening the window first when the download button is clicked, and then change the URL later. Something like this:
downloadAttachment() {
const downloadWindow = window.open('about:blank');
this.service.GetDownloadUrl(…).subscribe(downloadUrl => {
downloadWindow.location.href = downloadUrl;
});
}