We're developing mobile application in Xamarin. How can next be accomplished in iOS:
user downloads file from url (http request is made to REST API, secured with Authentication Basic username:secretKey)
file is saved to iOS
user opens a file (allowed are jpg, png, pdf, doc, docx, png)
file is opened in default application (e.g. for images image viewer)
As file operations are platform specific, here's interface definition:
public interface IFileHelper
{
void DownloadFileAndSave(Models.DocumentModel document);
}
Android implementation:
public class FileHelper : IFileHelper
{
// download file and view status in download manager
public void DownloadFileAndSave(Models.DocumentModel document)
{
DownloadManager dm = (DownloadManager)Android.App.Application.Context.GetSystemService(Context.DownloadService);
string url = WebApiUtils.GetBaseUrl() + string.Format("Api/v1/Dms/{0}", document.UniqueId);
DownloadManager.Request request = new Android.App.DownloadManager.Request(Android.Net.Uri.Parse(url)));
request.AddRequestHeader("Authorization", "Basic " + WebApiUtils.GetEncodedCredentials(Auth.Users.Current));
var downloadFile = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads);
string path = Path.Combine(downloadFile.AbsolutePath, document.FileName);
request.SetDestinationUri(Android.Net.Uri.FromFile(new Java.IO.File(path)));
request.SetMimeType(document.ContentType);
request.SetNotificationVisibility(DownloadVisibility.VisibleNotifyCompleted);
dm.Enqueue(request);
}
In Android file is simply stored on the filesystem and with File Explorer which is by default installed on any Android (i.e. My Files -> device storage -> Download), the file is opened in default application for file's mime type. Everything fine on Android.
Apple iOS implementation:
public class FileHelper : IFileHelper
{
public void DownloadFileAndSave(Models.DocumentModel document)
{
WebClient webClient = new WebClient();
webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + WebApiUtils.GetEncodedCredentials(Auth.Users.Current));
webClient.DownloadDataAsync(new System.Uri(WebApiUtils.GetBaseUrl() + string.Format(Consts.ApiUrls.GetDocument, document.UniqueId)));
webClient.DownloadDataCompleted += (sender, e) =>
{
byte[] content = e.Result;
string path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), document.FileName);
// doesn't throw exception therefore saved ok
File.WriteAllBytes(path, content);
Uri uri = new Uri(String.Format("file://{0}", path));
// doesn't work.
Device.OpenUri(uri);
};
}
}
Is there any other way to open downloaded file in default application. If I open url e.g. http://example.com/files/file1.png it opens the file in safari, but I can't put Authorization: Basic headers in Device.OpenUri.
I read about Load Non-Web Documents with WebView but you would have to build each file as BundleResource.
As Code Warrior commented there is one approach posted on link: https://forums.xamarin.com/discussion/36964/why-is-it-that-nothing-is-working-to-open-an-existing-local-pdf-file-in-the-ios-portion-of-my-pcl.
But Save image action doesn't work, everything else seems to work.
public void DownloadFileAndSave(Models.DocumentModel document)
{
WebClient webClient = new WebClient();
webClient.Headers.Add(HttpRequestHeader.Authorization, "Basic " + WebApiUtils.GetEncodedCredentials(Auth.Users.Current));
string tempPath = Path.GetTempPath();
string localFilename = Path.GetFileName(document.FileName);
string localPath = Path.Combine(tempPath, localFilename);
webClient.DownloadFileCompleted += (sender, e) =>
{
Device.BeginInvokeOnMainThread(() =>
{
QLPreviewItemFileSystem prevItem = new QLPreviewItemFileSystem(localFilename, localPath); // ql = quick look
QLPreviewController previewController = new QLPreviewController()
{
DataSource = new PreviewControllerDS(prevItem)
};
UIApplication.SharedApplication.KeyWindow.RootViewController.PresentViewController(previewController, true, null);
});
};
// download file
Uri uri = new System.Uri(WebApiUtils.GetBaseUrl() + string.Format(Consts.ApiUrls.GetDocument, document.UniqueId));
webClient.DownloadFileAsync(uri, localPath);
}
When Save image is triggered I get next error:
2017-10-03 13:45:56.797 MyApp.iOS[477:61030] Video
/private/var/mobile/Containers/Data/Application/33D7139A-53E0-4A2E-8C78-D3D13A2259B0/tmp/water-h2o-md.png
cannot be saved to the saved photos album: Error
Domain=AVFoundationErrorDomain Code=-11828 "Cannot Open"
UserInfo={NSUnderlyingError=0x1c0445d60 {Error
Domain=NSOSStatusErrorDomain Code=-12847 "(null)"},
NSLocalizedFailureReason=This media format is not supported.,
NSURL=file:///private/var/mobile/Containers/Data/Application/33D7139A-53E0-4A2E-8C78-D3D13A2259B0/tmp/water-h2o-md.png,
NSLocalizedDescription=Cannot Open}
iOS treates image as video? Is this a bug on iOS or am I something missing.
UPDATE
It turns out that next permissions was missing in Info.plist file:
<key>NSPhotoLibraryUsageDescription</key>
<string>Application needs to access photos</string>
<!-- for iOS 11 -->
<key>NSPhotoLibraryAddUsageDescription</key>
<string>Application needs to access photos</string>
Now Save Image action is working ok. But seriously Apple could return more appropriate error than Video image.jpg cannot be saved ...
Related
I am trying to download files from azure to computer via an web app. It works when I run locally the project, but when uploaded to ftp server it does not download.
I have tried Environment.SpecialFolder.Peronal, Desktop, etc.
public async Task<bool> DownloadBlobAsync(string file, string fileExtension, string directory)
{
string downlaodPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
_container = _client.GetContainerReference(containerName);
_directoy = _container.GetDirectoryReference(directory);
CloudBlockBlob blockBlob = _directoy.GetBlockBlobReference(file + "." + fileExtension);
using (var fileStream = File.OpenWrite(downlaodPath + "/"+ file + "." + fileExtension))
{
await blockBlob.DownloadToStreamAsync(fileStream);
return true;
}
}
The expected output should be on the documents or desktop.
The issue that you are seeing is due to the fact that your code is executing on the webserver, not on the clients (users) machine.
In other words, when you try to save to Environment.SpecialFolder.Personal, you're trying to save it to that folder on the web server, not the users desktop computer.
What you need to do is return the content of the blob in the request, and let the browser save the file - the user is likely to be prompted (depending on their browser settings) where exactly to save it. You should not be specifying this.
Here is an example of how to do this:
public async Task<HttpResponseMessage> DownloadBlobAsync(string file, string fileExtension, string directory)
{
_container = _client.GetContainerReference(containerName);
_directoy = _container.GetDirectoryReference(directory);
CloudBlockBlob blockBlob = _directoy.GetBlockBlobReference(file + "." + fileExtension);
using (var ms = new MemoryStream())
{
await blockBlob.DownloadToStreamAsync(ms);
var result = new HttpResponseMessage(HttpStatusCode.OK)
{
Content = new ByteArrayContent(ms.ToArray())
};
result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment")
{
FileName = "somefilename.ext"
};
result.Content.Headers.ContentType = new MediaTypeHeaderValue(blockBlob.Properties.ContentType);
return result;
}
}
Note that this is inefficient, as it will download the blob first to the webserver, and then return that to the client. It should be enough to get started.
When this endpoint is hit by the browser, the user will be prompted to save the file somewhere on their PC.
I have written a small method that uploads files on Dropbox and that method is working absolutely fine but the issue is how can I get the URL of the uploaded image so that I hit that URL on browser and it shows me the image.
Here is the code of uploading files:
public static async Task Run()
{
var accessToken = ConfigurationManager.AppSettings["DropBoxAccessToken"];
using (var dbx = new DropboxClient(accessToken))
{
//var full = await dbx.Users.GetCurrentAccountAsync();
await Upload(dbx, "/Test", "Test Image.jpg");
}
}
static async Task Upload(DropboxClient dbx, string folder, string file)
{
var readContent = System.IO.File.ReadAllBytes(#"D:\Images\IMG_20161127_204200968.jpg");
using (var mem = new MemoryStream(readContent))
{
var updated = await dbx.Files.UploadAsync(
folder + "/" + file,
WriteMode.Overwrite.Instance,
body: mem);
var mediaInfo = updated.MediaInfo;
}
}
I have tried to get details of uploaded image by hovering on var updated but didn't get the URL.
Any help?
To get a link to a file via the Dropbox API, you have two options:
GetTemporaryLinkAsync: this returns a temporary link, but isn't meant for being displayed in the browser directly.
CreateSharedLinkWithSettingsAsync: this returns a shared link that points to a preview page for the file that can be displayed in the browser.
I'm working in a function to download pdfs from DropBox, I'm using ASP.net Core , everything works good. The only thing is that when you click in the download link it doesn't show any message and downloads the file. I would like to show the download progress like usually happens when we download something from Internet, I don't want any dialog to appear, just to show that the file was downloaded like normally happens in any browser like Chrome or IE and then have the choices 'Show in Folder' and things like that, what would I need to add?
public async Task DownloadPdf()
{
DropboxClient client2 = new DropboxClient("cU5M-a4exaAAAAAAAAABDVZsKdpPteNmwHslOeFEo-HByuOr4v4ONvXoAMCFyOXH");
string folder = "MyFolder";
string file = "Test PDF.pdf";
using (var response = await client2.Files.DownloadAsync("/" + folder + "/" + file))
{
using (var fileStream = System.IO.File.Create(#"C:\Users\User\Downloads\Test.pdf"))
{
(await response.GetContentAsStreamAsync()).CopyTo(fileStream);
}
}
}
I have a asp.net core project with an API that returns a file:
[HttpGet("{id}")]
public IActionResult Get(int id) {
byte[] fileContent = READ_YOUR_FILE();
FileContentResult result = new FileContentResult(fileContent, "application/octet-stream") {
FileDownloadName = id.ToString()
};
return result;
}
If I access in my browser the URL from this API (myapp/api/mycontroller/id), then I can see the file downloading.
I have PDF file placed on different (FILE-Server) server machine, and the IIS machine on which my MVC application is hosted have rights to that File-Server. From IIS machine i can access the file through following URI:
file://file-server/data-folder/pdf/19450205.pdf
I want to enable my MVC app's users to download their respective files by clicking on download link or button. So probably i would have to write some Action for that link/button.
I tried to use File return type for my Action method in following way:
public ActionResult FileDownload()
{
string filePth = #"file://file-server/data-folder/pdf/19450205.pdf";
return File(filePth , "application/pdf");
}
but the above code gives exception of URI not supported.
I also tried to use FileStream to read bytes inside array return that bytes towards download, but FileStream also gives error of not proper "Virtual Path" as the file is not placed inside virtual path, its on separate server.
public ActionResult Download()
{
var document = = #"file://file-server/data-folder/pdf/19450205.pdf";
var cd = new System.Net.Mime.ContentDisposition
{
// for example foo.bak
FileName = document.FileName,
// always prompt the user for downloading, set to true if you want
// the browser to try to show the file inline
Inline = false,
};
Response.AppendHeader("Content-Disposition", cd.ToString());
return File(document.Data, document.ContentType);
}
Thanks for the replies, but both suggestion did not work.
as file needs to be accessed over URI, using FileInfo gives error: URI formats are not supported.
I managed to get this done through following mechanism:
public ActionResult FaxFileDownload()
{
string filePth = #"file://file-server/data-folder/pdf/19450205.pdf";
WebClient wc = new WebClient();
Stream s = wc.OpenRead(filePth);
return File(s, "application/pdf");
}
Thanks to All.
User uses my browser based on CefSharp. He uploads a file (which is selected in file input HTML control) to known server using XMLHttpRequest (it's JavaScript object for AJAX requests). I want to intercept and to read the uploading file in my browser.
I do the same in my browser based on Awesomium using IResourceInterceptor. It's easy, because ResourceRequest parameter contains full local path of the file. How can I do the same in CefSharp browser?
How user uploads file using XMLHttpRequest (JavaScript):
var file = document.getElementById('fileInput').files[0];
var formData = new FormData();
formData.append('file', file);
var xhr = new XMLHttpRequest();
xhr.responseType = 'arraybuffer';
xhr.open('POST', '/upload', true);
xhr.send(formData);
Awesomium way to intercept user's file (C#):
class MyResourceInterceptor : IResourceInterceptor
{
public ResourceResponse OnRequest(ResourceRequest request)
{
// intercept URL with /upload path only
if (request.Url.AbsolutePath != "/upload")
{
return null;
}
// full local path for user's file
var filePath = request[1].FilePath;
// now I can read and process the file
}
public bool OnFilterNavigation(NavigationRequest request)
{
return false;
}
}
CefSharp doesn't currently expose a way to access Post Data which is what I'm guessing you require.
I have implemented a PR that contains a basic implementation, feel free to test it out. See https://github.com/cefsharp/CefSharp/pull/1113
The other option you have is implement OnFileDialog, you'd have to display your own dialog, simple enough though.
https://github.com/cefsharp/CefSharp/blob/cefsharp/41/CefSharp/IDialogHandler.cs#L38