I have this code:
WebClient webClient = new WebClient();
webClient.DownloadFileAsync(new Uri("http://MySite.com/Desktop/Pics.png"),
#"c:\users\Windows\desktop\DesktopsPics\Desktop.png");
My Program will download a .png picture every day as "Daily Pics" in a folder! So, when a user clicks on a button, if "Daily Pic" is already exists in server, the program will download this file.
I can do this with the above code, but, if Pic.Png is not already exists in server, my program throws an error. It downloads a .html file that reads 404 not found.
How can I download a file, if this file exist on a server?
Since you are downloading the file Async, you will need to add an event handler for when the download is completed. Then you can inspect the arg for errors.
Try this:
static void Main(string[] args)
{
WebClient client = new WebClient();
client.DownloadFileCompleted += new AsyncCompletedEventHandler(DownloadCompleted);
string fileName = Path.GetTempFileName();
client.DownloadFileAsync(new Uri("https://www.google.com/images/srpr/logo11w.png"), fileName, fileName);
Thread.Sleep(20000);
Console.WriteLine("Done");
}
private static void DownloadCompleted(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
// inspect error message for 404
Console.WriteLine(e.Error.Message);
if (e.Error.InnerException != null)
Console.WriteLine(e.Error.InnerException.Message);
}
else
{
// We have a file - do something with it
WebClient client = (WebClient)sender;
// display the response header so we can learn
foreach(string k in client.ResponseHeaders.AllKeys)
{
Console.Write(k);
Console.WriteLine(": {0}", client.ResponseHeaders[k]);
}
// since we know it's a png, let rename it
FileInfo temp = new FileInfo((string)e.UserState);
string pngFileName = Path.Combine(Path.GetTempPath(), "DesktopPhoto.png");
if (File.Exists(pngFileName))
File.Delete(pngFileName);
File.Move((string)e.UserState, pngFileName); // move to where ever you want
Process.Start(pngFileName);
}
}
Related
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.
I used WebClient to download a file in my apk. But I got this error:
Unhandled Exception:
System.Net.WebException: An exception occurred during a WebClient request.
And this is the code that I tried:
{
using (WebClient client = new WebClient())
{
client.DownloadFile(
"https://code.org/images/social-media/code-2018-creativity.png",
#"j:\storage\emulated\legacy\Download\code-2018-creativity.png");
}
}
Since you are only referring to a WebException, it may have to do with one of these cases:
The URI formed by combining BaseAddress and address is invalid.
The file or destination folder does not exist. Make sure your path to
the destination folder already exists and that you have permissions to access it.
An error occurred while
downloading data.
If you provide us more information about the exception we may be able to reduce the error to one of these cases. To get the InnerException you can do something like this:
{
using (WebClient client = new WebClient ())
{
try
{
client.DownloadFile (
"https://code.org/images/social-media/code-2018-creativity.png",
#"j:\storage\emulated\legacy\Download\code-2018-creativity.png");
}
catch (Exception ex)
{
while (ex != null)
{
Console.WriteLine (ex.Message);
ex = ex.InnerException;
}
}
}
}
You have to ask permissions on run time even you have mentioned them in your manifest file if you are running Android api level 23 or greater.
Have a look at this blog would help about how to ask a run time permission:requesting-runtime-permissions-in-android
Also, this is the official sample of how to check RuntimePermissions
Refer: xamarin-system-unauthorizedaccessexception-access-to-the-path-is-denied
Update:
To ask run time permissions, you can use this plugin:Plugin.Permissions, install it to your project.
And then, call CheckMyPermissionAsync(); before you download the file:
private void FabOnClick(object sender, EventArgs eventArgs)
{
View view = (View) sender;
CheckMyPermissionAsync();
}
In the method CheckMyPermissionAsync(), check your Storage permission and then download file:
public async void CheckMyPermissionAsync()
{
var permissionsStartList = new List<Permission>()
{
Permission.Storage
};
var permissionsNeededList = new List<Permission>();
try
{
foreach (var permission in permissionsStartList)
{
var status = await CrossPermissions.Current.CheckPermissionStatusAsync(permission);
if (status != PermissionStatus.Granted)
{
permissionsNeededList.Add(permission);
}
}
}
catch (Exception ex)
{
}
var results = await CrossPermissions.Current.RequestPermissionsAsync(permissionsNeededList.ToArray());
//Check the persimmison again
var storeagePermission = await CrossPermissions.Current.CheckPermissionStatusAsync(Permission.Storage);
if (storeagePermission == PermissionStatus.Granted)
{
//Download file here
DownloadFile("http://www.dada-data.net/uploads/image/hausmann_abcd.jpg", "XF_Downloads");
}
else {
Console.WriteLine("No permissions");
}
}
You can check the result in the completed event:
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if (e.Error != null)
{
Console.WriteLine("success");
}
else
{
if (OnFileDownloaded != null) { }
Console.WriteLine("fail");
}
}
Note: pay attention to your filePath,make sure your path is correct, I use:
string pathToNewFolder = Path.Combine(Android.OS.Environment.ExternalStorageDirectory.AbsolutePath, folder);
I updated my sample here: runtime-permission-xamarin.android
I'm making a WPF application.
I'm using WebClient to download files. I have a list of the name of the files that should be downloaded from a current path. I use an foreach to iterate through each name and then download each file sequency. The name of the file i get from a torrent file which i decode.
public class DownloadGameFile
{
private DownloadTorrentFile DLTorrent;
//List of file that already exist
private List<string> ExistFile = new List<string>();
DirectoryInfo fileInfo;
private volatile bool _completed;
private string savePath = #"C:\Program Files (x86)\program\Client\package\downloads\";
public DownloadGameFile()
{
DLTorrent = new DownloadTorrentFile();
fileInfo = new DirectoryInfo(savePath);
}
public bool StartDownload(int torrentId)
{
try
{
DLTorrent.DecodeTorrent(torrentId);
//File info from a Directory
FileInfo[] files = fileInfo.GetFiles();
foreach (FileInfo i in files)
{
Console.WriteLine("Files exit ");
if (DLTorrent.GameInfomation[i.Name] != i.Length)
{
i.Delete();
}
else
{
Console.WriteLine("add files ");
ExistFile.Add(i.Name);
}
}
//Make a list which file not downloaded yet
var res = DLTorrent.GameInfomation.Keys.Except(ExistFile);
foreach (var x in res)
{
Console.WriteLine(x);
}
foreach (var x in res)
{
DownloadProtocol("http://cdn.path.com/rental/" + torrentId + "/" + x, savePath + x);
}
return true;
}
catch
{
return false;
}
}
public void DownloadProtocol(string address, string location)
{
WebClient client = new WebClient();
Uri Uri = new Uri(address);
client.DownloadFileCompleted += new AsyncCompletedEventHandler(Completed);
client.DownloadProgressChanged += new DownloadProgressChangedEventHandler(DownloadProgress);
client.DownloadFileAsync(Uri, location);
}
private void DownloadProgress(object sender, DownloadProgressChangedEventArgs e)
{
// Displays the operation identifier, and the transfer progress.
Console.WriteLine("{0} downloaded {1} of {2} bytes. {3} % complete...",
(string)e.UserState,
e.BytesReceived,
e.TotalBytesToReceive,
e.ProgressPercentage);
}
private void Completed(object sender, AsyncCompletedEventArgs e)
{
if (e.Cancelled == true)
{
Console.WriteLine("Download has been canceled.");
}
else
{
Console.WriteLine("Download completed!");
}
}
This code work fine in an Console app with a current thread blocker. But when I use the same code in an WPF app It doesn't. I'm using a button to execute the StartDownload() function, but when I do that it start downloading all the files at the same time. Example the first file get 3% done and then it switch to another file and so on. I really don't know why this isn't working.
Have you considered not using .DownloadFileAsync? You can try .DownloadFile and start DownloadProtocol() with a single background thread. Although I think you'll have to rethink your DownloadProgress output.
I do something very similar within a winform.
I am having problem downloading files using Background transfer. After completion of download when moving file, it gives you an exception Operation not permitted
void addTransferRequest(string fileName)
{
if (string.IsNullOrEmpty(fileName))
return;
string filePathToDownload = string.Empty;
filePathToDownload = activeReciter.DownloadURL;
filePathToDownload += fileName;
Uri transferUri = new Uri(Uri.EscapeUriString(filePathToDownload),
UriKind.RelativeOrAbsolute);
BackgroundTransferRequest transferRequest = new
BackgroundTransferRequest(transferUri);
transferRequest.Method = "GET";
transferRequest.TransferPreferences = TransferPreferences.AllowBattery;
Uri downloadUri = new Uri(DataSource.TEMPDOWNLOADLOCATION + fileName,
UriKind.RelativeOrAbsolute);
transferRequest.DownloadLocation = downloadUri;
transferRequest.Tag = fileName;
transferRequest.TransferStatusChanged +=
new EventHandler<BackgroundTransferEventArgs>
(transfer_TransferStatusChanged);
transferRequest.TransferProgressChanged += new
EventHandler<BackgroundTransferEventArgs>(transfer_TransferProgressChanged);
try
{
BackgroundTransferService.Add(transferRequest);
chapterFileNames.Dequeue();
}
catch (InvalidOperationException)
{
}
catch (Exception)
{
}
}
void transfer_TransferStatusChanged(object sender, BackgroundTransferEventArgs e)
{
ProcessTransfer(e.Request);
}
void transfer_TransferProgressChanged(object sender, BackgroundTransferEventArgs e)
{
}
private void ProcessTransfer(BackgroundTransferRequest transfer)
{
switch (transfer.TransferStatus)
{
case TransferStatus.Completed:
if (transfer.StatusCode == 200 || transfer.StatusCode == 206)
{
using (IsolatedStorageFile isoStore =
IsolatedStorageFile.GetUserStoreForApplication())
{
try
{
string filename = transfer.Tag;
string folderPath = string.Format(#"{0}{1}\{2}\",
DataSource.DOWNLOADLOCATION, activeReciter.ReciterID, chapter.ChapterID);
string fileFullPath = folderPath + filename;
if (!isoStore.DirectoryExists(Path.GetDirectoryName(folderPath)))
isoStore.CreateDirectory(Path.GetDirectoryName(folderPath));
if (isoStore.FileExists(fileFullPath))
isoStore.DeleteFile(fileFullPath);
isoStore.MoveFile(transfer.DownloadLocation.OriginalString, fileFullPath);
//Excpetion is thrown here
RemoveTransferRequest(transfer.RequestId);
}
catch (Exception ex)
{
MessageBox.Show("Error Occured: " + ex.Message + transfer.Tag, "Error",
MessageBoxButton.OK);
return;
}
}
}
break;
}
}
When moving file, it throws exception, I don't know what is wrong with moving (this happens on some of the files not all files).
From the MSDN Page, under File System Restrictions section:
You can create any additional directory structure you choose
underneath the root “/shared/transfers” directory, and you can copy or
move files after the transfer is complete to ensure that the
background transfer service does not modify the files, but attempting
to initiate a transfer using a path outside of the “/shared/transfers”
directory will throw an exception.
Make sure you are not trying to move your file outside from the /Shared/Transfers folder.
I have a form which has radiobuttons, each of them is giving a file name in a string, and what I want to do is to have that string as a name for any file that a user uploads.
It'll be great if you could explain to me how to rename, because I already got it the code to upload or just help me modify this function, I would probably have to add to the parameters "string type" tho:
public void uploadFTP(string filename, string type, string password, ProgressBar progressbar)
{
WebClient client = new WebClient();
client.Credentials = new System.Net.NetworkCredential("username", password);
try
{
client.UploadFileAsync(new Uri(#"ftp://ftpaddress.com" + "/" + new FileInfo(filename).Name), "STOR", filename);
}
catch(System.InvalidCastException)
{
// Handled it
}
catch (System.ArgumentException)
{
// Handled it
}
client.UploadProgressChanged += (s, e) =>
{
progressbar.Value = e.ProgressPercentage;
};
client.UploadFileCompleted += (s, e) =>
{
MessageBox.Show("Upload complete", "Message", MessageBoxButtons.OK, MessageBoxIcon.Information, MessageBoxDefaultButton.Button1);
};
}
If it's important: The files are RTF (RichTextBox).
Thanks!
Just upload to different URL then. Replace new FileInfo(filename).Name in your code with the name you actually want. Also, I think not using string manipulation is better. And the STOR command is the default.
client.UploadFileAsync(new Uri(new Uri("ftp://ftpaddress.com"), newName)), filename);