How can I show/find downloaded image from xamarin.ios in iPhone? - c#

We have written a code to download images from uri using webclient in xamarin.ios. When we download an image it's not displayed in Gallery/Photo app, or in any other location of the iPhone.
Here is my download code:
public void DownloadFiles()
{
try
{
var webClient = new WebClient();
webClient.DownloadDataCompleted += (s, e) => {
var bytes = e.Result;
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string localFilename = "downloaded.png";
string localPath = Path.Combine(documentsPath, localFilename);
Console.WriteLine("localPath:" + localPath);
File.WriteAllBytes(localPath, bytes);
// IMPORTANT: this is a background thread, so any interaction with
// UI controls must be done via the MainThread
InvokeOnMainThread(() => {
// imageView.Image = UIImage.FromFile(localPath);
UIAlertView alert = new UIAlertView()
{
Title = "alert title",
Message = "Download successfully"
};
alert.AddButton("OK");
alert.AddButton("Cancel");
alert.Show();
});
};
var url = new Uri("https://www.xamarin.com/content/images/pages/branding/assets/xamagon.png");
webClient.DownloadDataAsync(url);
}
catch(Exception e)
{
throw e;
}
}
Environment.GetFolderPath(Environment.SpecialFolder.Personal);
how to find this path?

We have added these permission in info.plist to solve this problem. "Privacy - Photo Library Additions Usage Description" and "Privacy - Photo Library Usage Description"

Related

C# Receiving console writeline in richtextbox from windows forms

I have looked at all similar questions. I cant seem to figure out why my situation does not work. I created a test app using the following class:
public class TextBoxStreamWriter : TextWriter
{
RichTextBox _output = null;
public TextBoxStreamWriter(RichTextBox output)
{
_output = output;
}
public override void Write(char value)
{
base.Write(value);
_output.AppendText(value.ToString()); // When character data is written, append it to the text box.
}
public override Encoding Encoding
{
get { return System.Text.Encoding.UTF8; }
}}
My Form Code
public partial class Form1 : Form
{
TextWriter _writer = null;
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
_writer = new TextBoxStreamWriter(richTextBox1);
// Redirect the out Console stream
Console.SetOut(_writer);
//Console.WriteLine("Now redirecting output to the text box");
}
private void button1_Click(object sender, EventArgs e)
{
// Writing to the Console now causes the text to be displayed in the text box.
Console.WriteLine("Hello world");
}
}
This works every time I click the button. Now the reason for my question is why does it work there and not in my project. I created a Youtube uploader app using API 3 it uploads completes but the console writeline show the messages but my richtextbox only shows my intial Video uploading message. Altimately I only want to capture the write for a couple of reasons Show any errors, Get progress to for progressbar and get video.ID when I call Console.writeline my richtextbox does not get updated heres the code being used
/// <summary>
/// Upload video to youtube
/// </summary>
/// <param name="sender"></param>
/// <param name="e"></param>
private void button2_Click(object sender, EventArgs e)
{
//lblstatus.Text = "uploading video";
Console.WriteLine("uploading video");
try
{
Thread thead = new Thread(() =>
{
Run().Wait();
});
thead.IsBackground = true;
thead.Start();
}
catch (AggregateException ex)
{
Console.WriteLine("Error: " + ex.Message);
// MessageBox.Show(ex.Message);
}
}
private async Task Run()
{
UserCredential credential;
using (var stream = new FileStream("client_secret", FileMode.Open, FileAccess.Read))
{
credential = await GoogleWebAuthorizationBroker.AuthorizeAsync(
GoogleClientSecrets.FromStream(stream).Secrets,
// This OAuth 2.0 access scope allows an application to upload files to the
// authenticated user's YouTube channel, but doesn't allow other types of access.
new[] { YouTubeService.Scope.YoutubeUpload },
"user",
CancellationToken.None
);
}
var youtubeService = new YouTubeService(new BaseClientService.Initializer()
{
HttpClientInitializer = credential,
ApplicationName = Assembly.GetExecutingAssembly().GetName().Name
});
var video = new Video();
video.Snippet = new VideoSnippet();
video.Snippet.Title = txtTitle.Text;
video.Snippet.Description = txtDescription.Text;
string[] tagSeo = Regex.Split(txtTagSeo.Text, ",");
video.Snippet.Tags = tagSeo;
video.Snippet.CategoryId = "22"; // See https://developers.google.com/youtube/v3/docs/videoCategories/list
video.Status = new VideoStatus();
video.Status.PrivacyStatus = "public"; // or "private" or "public"
var filePath = txtPath.Text; // Replace with path to actual movie file.
Console.WriteLine(filePath);
using (var fileStream = new FileStream(filePath, FileMode.Open))
{
var videosInsertRequest = youtubeService.Videos.Insert(video, "snippet,status", fileStream, "video/*");
videosInsertRequest.ProgressChanged += videosInsertRequest_ProgressChanged;
videosInsertRequest.ResponseReceived += videosInsertRequest_ResponseReceived;
await videosInsertRequest.UploadAsync();
}
}
void videosInsertRequest_ProgressChanged(Google.Apis.Upload.IUploadProgress progress)
{
switch (progress.Status)
{
case UploadStatus.Uploading:
Console.WriteLine("{0} bytes sent.", progress.BytesSent);
//lblstatus.Text = String.Format("{0} bytes sent.", progress.BytesSent);
break;
case UploadStatus.Failed:
//lblstatus.Text = String.Format("An error prevented the upload from completing.{0} ", progress.Exception);
Console.WriteLine("An error prevented the upload from completing.\n{0}", progress.Exception);
break;
}
}
void videosInsertRequest_ResponseReceived(Video video)
{
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
MessageBox.Show("Video id '{0}' was successfully uploaded.", video.Id);
}
I was unable to make the above code work so I went to plan B. I started with getting the Video ID only because I only get 6 tries per day I choose to use the Video ID as string. I eliminated the TextBoxStreamWriter class all together and went with creating a new thread and delegate. I will work on the progressbar value next. here's how I did it for anyone else who may have a similar problem.
Created a delegate to update my video ID Label as follows
public delegate void LabelDelegate(string s);
void UpdateVideoID(string text)
{
if (lblVideoID.InvokeRequired)
{
LabelDelegate LDEL = new LabelDelegate(UpdateVideoID);
lblVideoID.Invoke(LDEL, text);
}
else
lblVideoID.Text = text;
}
void videosInsertRequest_ResponseReceived(Video video)
{
Thread th = new Thread(() => UpdateVideoID(video.Id));
th.Start();
Console.WriteLine("Video id '{0}' was successfully uploaded.", video.Id);
}

Image does not display in gallery after download

I am developing a Xamarin app which retrives info from DB, take/choose photo and upload them to remote server, display this images from the remote server and the user can delete them by tap on and press a button and download the images from the remote server to the local device.
Everything works without problem, but when I download the image and after I go to the gallery for check it, the image does not appear, whereas I can see it and open in the file explorer. When I reboot the phone, the image appear in the gallery.
Below my current button download method:
private void button_download_image_Clicked(object sender, EventArgs e)
{
Uri image_url_format = new Uri(image_url);
WebClient webClient = new WebClient();
try
{
byte[] bytes_image = webClient.DownloadData(image_url_format);
Stream image_stream = new MemoryStream(bytes_image);
string dest_folder = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).ToString();
string file_name = System.IO.Path.GetFileName(image_url_format.LocalPath);
string dest_path = System.IO.Path.Combine(dest_folder, file_name);
using (var fileStream = new FileStream(dest_path, FileMode.Create, FileAccess.Write))
{
image_stream.CopyTo(fileStream);
}
}
catch (Exception ex)
{
DisplayAlert("Error", ex.ToString(), "OK");
}
DisplayAlert("Alert", "Download completed!", "OK");
}
I tried in another device, but I got the same behavior.
Probably there is a sort of thing which does not refresh the gallery.
Any idea how to force the gallery to refresh or something similar?
You need to refresh your gallery after inserting or deleting any pictures in storage.
You can try this.
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(dest_path)));
Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent);
Add these lines below your code.
Make it like
private void button_download_image_Clicked(object sender, EventArgs e)
{
Uri image_url_format = new Uri(image_url);
WebClient webClient = new WebClient();
try
{
byte[] bytes_image = webClient.DownloadData(image_url_format);
Stream image_stream = new MemoryStream(bytes_image);
string dest_folder = Android.OS.Environment.GetExternalStoragePublicDirectory(Android.OS.Environment.DirectoryDownloads).ToString();
string file_name = System.IO.Path.GetFileName(image_url_format.LocalPath);
string dest_path = System.IO.Path.Combine(dest_folder, file_name);
using (var fileStream = new FileStream(dest_path, FileMode.Create, FileAccess.Write))
{
image_stream.CopyTo(fileStream);
}
var mediaScanIntent = new Intent(Intent.ActionMediaScannerScanFile);
mediaScanIntent.SetData(Android.Net.Uri.FromFile(new Java.IO.File(dest_path)));
//for old xamarin forms version
Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent);
//for new xamarin forms version
//Android.App.Application.SendBroadcast(mediaScanIntent);
}
catch (Exception ex)
{
DisplayAlert("Error", ex.ToString(), "OK");
return;
}
DisplayAlert("Alert", "Download completed!", "OK");
}
You need to just refresh the file you have downloaded. It's helpful.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT){
Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
File f = new File("file://"+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
Uri contentUri = Uri.fromFile(f);
mediaScanIntent.setData(contentUri);
this.sendBroadcast(mediaScanIntent);
}else{
sendBroadcast(new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + Environment.getExternalStorageDirectory())));
}
Make sure required permission given on both platforms.
Use in your class:
bool success = await DependencyService.Get<IPhotoLibrary>().SavePhotoAsync(data, folder, filename);
Common Interface
public interface IPhotoLibrary
{
Task<bool> SavePhotoAsync(byte[] data, string folder, string filename);
}
In Android service
public async Task<bool> SavePhotoAsync(byte[] data, string folder, string filename)
{
try
{
File picturesDirectory = Environment.GetExternalStoragePublicDirectory(Environment.DirectoryPictures);
File folderDirectory = picturesDirectory;
if (!string.IsNullOrEmpty(folder))
{
folderDirectory = new File(picturesDirectory, folder);
folderDirectory.Mkdirs();
}
using (File bitmapFile = new File(folderDirectory, filename))
{
bitmapFile.CreateNewFile();
using (FileOutputStream outputStream = new FileOutputStream(bitmapFile))
{
await outputStream.WriteAsync(data);
}
// Make sure it shows up in the Photos gallery promptly.
MediaScannerConnection.ScanFile(MainActivity.Instance,
new string[] { bitmapFile.Path },
new string[] { "image/png", "image/jpeg" }, null);
}
}
catch (System.Exception ex)
{
return false;
}
return true;
}
In iOS service:
public Task<bool> SavePhotoAsync(byte[] data, string folder, string filename)
{
NSData nsData = NSData.FromArray(data);
UIImage image = new UIImage(nsData);
TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
image.SaveToPhotosAlbum((UIImage img, NSError error) =>
{
taskCompletionSource.SetResult(error == null);
});
return taskCompletionSource.Task;
}
also you can refer this one just to save an image and to reflect it in media, no need to use skiasharp for that. https://learn.microsoft.com/en-us/xamarin/xamarin-forms/user-interface/graphics/skiasharp/bitmaps/saving
Hope this may resolve your issue.
Refer to Blu's answer,
I changed this Xamarin.Forms.Forms.Context.SendBroadcast(mediaScanIntent); to Android.App.Application.Context.SendBroadcast(mediaScanIntent); and all works.

How to upload a file with POST call to external API

I am trying to integrate the use of an external API to perform POST and GET calls with my C# WPF client application. https://developers.virustotal.com/reference.
This is what i have right now:
ScanPage.xaml.cs
private void browseFileButton_Click(object sender, RoutedEventArgs e)
{
OpenFileDialog openFileDialog = new OpenFileDialog();
openFileDialog.Multiselect = false;
openFileDialog.Filter = "All files (*.*)|*.*";
openFileDialog.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
if (openFileDialog.ShowDialog() == true)
{
filePath = openFileDialog.FileName;
fileLocation.Text = filePath;
}
}
private async void uploadFileButton_Click(object sender, RoutedEventArgs e)
{
if (filePath != null)
{
var resultInfo = await InfoProcessor.PostInfo(filePath);
responseText.Text = "File queued for scanning, please wait a moment...";
var resultInfo2 = await InfoProcessor.LoadInfo(resultInfo.Resource);
System.Threading.Thread.Sleep(10000);
//await Task.Delay(5000).ContinueWith(t => runLoadInfo(resultInfo.Resource));
responseText.Text = $"Scan Completed, MD5 checksum of file is {resultInfo2.Sha256} \n {resultInfo2.Positives} out of {resultInfo2.Total} {resultInfo2.Permalink} scan engines has detected to be potentially malicious";
}
else
{
MessageBox.Show("please upload a file");
}
}
public static async Task<InfoModel> PostInfo(string fileString)
{
string url = "https://www.virustotal.com/vtapi/v2/file/scan";
string apiKey = "xxxxxx";
string uploadedFile = fileString;
var values = new Dictionary<string, string>
{
{ "apikey", apiKey },
{ "file", uploadedFile}
};
var content = new FormUrlEncodedContent(values);
using (HttpResponseMessage response = await ApiStartup.ApiClient.PostAsync(url, content))
{
InfoModel info = await response.Content.ReadAsAsync<InfoModel>();
return info;
}
//var response = await ApiStartup.ApiClient.PostAsync(url, content);
//var responseString = await response.Content.ReadAsStringAsync();
//HttpResponseMessage response = await ApiStartup.ApiClient.PostAsJsonAsync("apikey",apiKey );
//response.EnsureSuccessStatusCode();
}
public static async Task<InfoModel> LoadInfo(string infoVar)
{
string apiKey = "xxxxxxxx";
//string resourceKey = "efbb7149e39e70c84fe72c6fe8cef5d379fe37e7238d19a8a4536fb2a3146aed";
string resourceKey = infoVar;
string url = $"https://www.virustotal.com/vtapi/v2/file/report?apikey={apiKey}&resource={resourceKey}";
using (HttpResponseMessage response = await ApiStartup.ApiClient.GetAsync(url))
{
if (response.IsSuccessStatusCode)
{
InfoModel info = await response.Content.ReadAsAsync<InfoModel>();
return info;
}
else
{
throw new Exception(response.ReasonPhrase);
}
}
}
After posting and getting the result of the scanned report of the uploaded file, it seems like i only upload just the string of the filepath and not the actual file itself. How can i code it so that the selected file would be properly posted instead? Thanks.
As for your latest comment, yes, you should use multipart form post.
You can implement it yourself, or use an external library to do this for you, like VirusTotalNet, as linked in the comments. If you still want to do it yourself, here is the code from this library:
https://github.com/Genbox/VirusTotalNet/blob/master/src/VirusTotalNet/VirusTotal.cs#L162-L217

Download txt file from google drive in windows phone 8.1

I'm doing a windows phone project, and need to download a text file from the internet and read its content.
This is what I have tried (but it didn't work)
private async Task pobierz()
{
string source = "https://drive.google.com/file/d/0BzgKBwKyU4oORkxxSlVITGswb1E/view?usp=sharing";
string LocalName = "hej.txt";
var srce = new Uri(source, UriKind.Absolute);
// var destinationFile =await KnownFolders.PicturesLibrary.CreateFileAsync()
StorageFile file = await StorageFile.GetFileFromApplicationUriAsync(new Uri(#"ms-appx:///Assets/hej.txt"));
var downloader = new BackgroundDownloader();
DownloadOperation download = downloader.CreateDownload(srce,file);
}
Please see https://msdn.microsoft.com/en-us/library/windows/apps/xaml/jj152726.aspx?f=255&MSPPError=-2147217396 for detailed description of how to use the BackgroundDownloader.
You need to implement and call the following method:
private async void HandleDownloadAsync(DownloadOperation download, bool start)
{
try
{
// Store the download so we can pause/resume.
activeDownloads.Add(download);
Progress<DownloadOperation> progressCallback = new Progress<DownloadOperation>(DownloadProgress);
if (start)
{
// Start the download and attach a progress handler.
await download.StartAsync().AsTask(cts.Token, progressCallback);
}
else
{
// The download was already running when the application started, re-attach the progress handler.
await download.AttachAsync().AsTask(cts.Token, progressCallback);
}
ResponseInformation response = download.GetResponseInformation();
Log(String.Format("Completed: {0}, Status Code: {1}", download.Guid, response.StatusCode));
}
catch (TaskCanceledException)
{
Log("Download cancelled.");
}
catch (Exception ex)
{
LogException("Error", ex);
}
finally
{
activeDownloads.Remove(download);
}
}

The process cannot access the file because it is being used by another process

I am running a program where a file gets uploaded to a folder in IIS,and then is processed to extract some values from it. I use a WCF service to perform the process, and BackgroundUploader to upload the file to IIS. However, after the upload process is complete, I get the error "The process cannot access the file x because it is being used by another process." Based on similar questions asked here, I gathered that the file concerned needs to be in a using statement. I tried to modify my code to the following, but it didn't work, and I am not sure if it is even right.
namespace App17
{
public sealed partial class MainPage : Page, IDisposable
{
private CancellationTokenSource cts;
public void Dispose()
{
if (cts != null)
{
cts.Dispose();
cts = null;
}
GC.SuppressFinalize(this);
}
public MainPage()
{
this.InitializeComponent();
cts = new CancellationTokenSource();
}
public async void Button_Click(object sender, RoutedEventArgs e)
{
try
{
Uri uri = new Uri(serverAddressField.Text.Trim());
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
using (var stream = await file.OpenAsync(FileAccessMode.Read))
{
GlobalClass.filecontent = file.Name;
GlobalClass.filepath = file.Path;
BackgroundUploader uploader = new BackgroundUploader();
uploader.SetRequestHeader("Filename", file.Name);
UploadOperation upload = uploader.CreateUpload(uri, file);
await HandleUploadAsync(upload, true);
stream.Dispose();
}
}
catch (Exception ex)
{
string message = ex.ToString();
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
}
private void CancelAll(object sender, RoutedEventArgs e)
{
Log("Canceling all active uploads");
cts.Cancel();
cts.Dispose();
cts = new CancellationTokenSource();
}
private async Task HandleUploadAsync(UploadOperation upload, bool start)
{
try
{
Progress<UploadOperation> progressCallback = new Progress<UploadOperation>(UploadProgress);
if (start)
{
await upload.StartAsync().AsTask(cts.Token, progressCallback);
}
else
{
// The upload was already running when the application started, re-attach the progress handler.
await upload.AttachAsync().AsTask(cts.Token, progressCallback);
}
ResponseInformation response = upload.GetResponseInformation();
Log(String.Format("Completed: {0}, Status Code: {1}", upload.Guid, response.StatusCode));
cts.Dispose();
}
catch (TaskCanceledException)
{
Log("Upload cancelled.");
}
catch (Exception ex)
{
string message = ex.ToString();
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
}
private void Log(string message)
{
outputField.Text += message + "\r\n";
}
private async void LogStatus(string message)
{
var dialog = new MessageDialog(message);
await dialog.ShowAsync();
Log(message);
}
private void UploadProgress(UploadOperation upload)
{
BackgroundUploadProgress currentProgress = upload.Progress;
MarshalLog(String.Format(CultureInfo.CurrentCulture, "Progress: {0}, Status: {1}", upload.Guid,
currentProgress.Status));
double percentSent = 100;
if (currentProgress.TotalBytesToSend > 0)
{
percentSent = currentProgress.BytesSent * 100 / currentProgress.TotalBytesToSend;
}
MarshalLog(String.Format(CultureInfo.CurrentCulture,
" - Sent bytes: {0} of {1} ({2}%), Received bytes: {3} of {4}", currentProgress.BytesSent,
currentProgress.TotalBytesToSend, percentSent, currentProgress.BytesReceived, currentProgress.TotalBytesToReceive));
if (currentProgress.HasRestarted)
{
MarshalLog(" - Upload restarted");
}
if (currentProgress.HasResponseChanged)
{
MarshalLog(" - Response updated; Header count: " + upload.GetResponseInformation().Headers.Count);
}
}
private void MarshalLog(string value)
{
var ignore = this.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
{
Log(value);
});
}
}
}
After this is done, the file name is sent to a WCF service which will access and process the uploaded file to extract certain values. It is at this point I receive the error. I would truly appreciate some help.
public async void Extract_Button_Click(object sender, RoutedEventArgs e)
{
ServiceReference1.Service1Client MyService = new ServiceReference1.Service1Client();
string filename = GlobalClass.filecontent;
string filepath = #"C:\Users\R\Documents\Visual Studio 2015\Projects\WCF\WCF\Uploads\"+ filename;
bool x = await MyService.ReadECGAsync(filename, filepath);
}
EDIT: Code before I added the using block
try
{
Uri uri = new Uri(serverAddressField.Text.Trim());
FileOpenPicker picker = new FileOpenPicker();
picker.FileTypeFilter.Add("*");
StorageFile file = await picker.PickSingleFileAsync();
GlobalClass.filecontent = file.Name;
GlobalClass.filepath = file.Path;
BackgroundUploader uploader = new BackgroundUploader();
uploader.SetRequestHeader("Filename", file.Name);
UploadOperation upload = uploader.CreateUpload(uri, file);
await HandleUploadAsync(upload, true);
}
When you work with stream writers you actually create a process, which you can close it from task manager. And after stream.Dispose() put stream.Close().
This should solve your problem.
You should also close the stream that writes the file to disk (look at your implementation of CreateUpload).
i got such error in DotNet Core 2 using this code:
await file.CopyToAsync(new FileStream(fullFileName, FileMode.Create));
counter++;
and this is how I managed to get rid of message (The process cannot access the file x because it is being used by another process):
using (FileStream DestinationStream = new FileStream(fullFileName, FileMode.Create))
{
await file.CopyToAsync(DestinationStream);
counter++;
}

Categories