I have a Xamarin Forms application in which I want to read a txt file. In my android project the file is placed in the assets folder with these properties :
Build action : Android Asset
Copy options : Allways copy.
And I am capabale to read the file with this code :
public string GetAppSetting(string name)
{
string retVal = "";
try
{
using (StreamReader sr = new StreamReader(AndroidHelper.Assets.Open("AppSettings.txt")))
{
var result = sr.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var line in result)
{
if (line.StartsWith(name + ":"))
return line.Split(':')[1];
}
}
}
catch (Exception ex)
{
ParseError(ex, "GetConnectionString");
}
return retVal;
}
On the other hand, in the uwp project, I have allways the exception File Not Found!
I put the file in the root of the project and tried to put in the assets folder too. It doesn't change the result. File Not Found!
Build action : Content (Tried other options too).
Copy options : Allways copy.
Here is my code to read the file :
private async Task<string> ReadFileAsync(string name)
{
string retVal = "parameter not found";
try
{
Windows.Storage.StorageFolder storageFolder = Windows.Storage.ApplicationData.Current.LocalFolder;
Windows.Storage.StorageFile sampleFile = await storageFolder.GetFileAsync("AppSettings.txt");
string str = await Windows.Storage.FileIO.ReadTextAsync(sampleFile);
foreach (var line in str.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries))
{
if (line.StartsWith(name + ":"))
return line.Split(':')[1];
}
}
catch (Exception ex)
{
MainHelper.ParseError(ex, "UWP readFileAsync");
}
return retVal;
}
What is wrong wiht my code ? Or where should I place the AppSettings.txt ?
For UWP,
I suggest you to set the file as an "Embedded Resource" (Build Action)
To ensure your file is loaded as an embedded resource at runtime, you can enumerate all your assembly's resources like this:
var resourceNames = anotherSameResAssemblyInstance.GetType()
.GetTypeInfo().Assembly
.GetManifestResourceNames();
Then, you can open the file as a stream like this:
string myFileResourceStream = "{YourAppNamespace}.AppSettings.txt";
var myFileResourceStream = someAssembly.GetManifestResourceStream(name);
Where 'YourAppNamespace' is the namespace in your app where is embedded the file. To get the correct full name, just check all values returned by GetManifestResourceNames() method.
Example:
var myFile = resourceNames.Where(x => x.Contains("AppSettings.txt")).FirstOrDefault();
if (myFile != null)
{
var str = this.GetType().GetTypeInfo().Assembly.GetManifestResourceStream(myFile);
}
It should now work.
In the UWP MainPage Xaml.cs change the LoadApplication:
namespace MyApp.UWP
{
public sealed partial class MainPage
{
public MainPage()
{
this.InitializeComponent();
LoadApplication(new MyApp.App(Windows.Storage.ApplicationData.Current.LocalFolder.Path));
}
}
}
Then in the app.Xaml.cs add:
public static string path;
public App(string paTh)
{
InitializeComponent();
path = paTh.ToString();
MainPage = new ContentPage();
}
Then use App.path as the Windows storage path.
Related
i wrote an application which is a custom console that allows execution of various commands. One of the commands allows to find a file's full path, according to part of its name. The input data is a string, which equals to part\full name of the file.
My question is - how to minimize the search code runtime complexity as much as possible?
Here is the command's code:
using CustomConsole.Common;
using System;
using System.Collections.Generic;
using System.IO;
namespace Shell_Commander.Commands
{
class FindFileCommand : ICommand
{
private string _findFileCommandName = "findfile";
public string Name { get { return _findFileCommandName; } set { _findFileCommandName = value; } }
public string Execute(string parameters)
{
var fileLocations = new Dictionary<string, bool>();
try
{
var splittedParameters = parameters.Split(" ");
var initialLocation = splittedParameters[0];
var fileName = splittedParameters[1];
foreach (var filePath in Directory.GetFiles(initialLocation, "*.*", SearchOption.AllDirectories))
{
fileLocations.Add(filePath, false);
if (Path.GetFileName(filePath) == fileName || Path.GetFileNameWithoutExtension(filePath) == fileName)
{
fileLocations[filePath] = true;
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
bool fileFound = false;
string returnedOutput = "";
foreach (var location in fileLocations.Keys)
{
if (fileLocations[location])
{
returnedOutput += $"The file found in path: {location}\n";
Console.Write(returnedOutput);
fileFound = true;
}
}
if (!fileFound)
{
returnedOutput = "The file not found in this path";
Console.WriteLine(returnedOutput);
return returnedOutput;
}
return returnedOutput;
}
}
}
Example - for the input parameters "c:\temp test", the output can be:
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.xml
The file found in path: c:\temp\test.json
The file found in path: c:\temp\test.xml
The file found in path: c:\temp\test\test.json
You can simply your foreach like this
var fileLocations = Directory.GetFiles(initialLocation, $"{filePath}.*", SearchOption.AllDirectories);
foreach (var location in fileLocations)
{
returnedOutput += $"The file found in path: {location}\n";
Console.Write(returnedOutput);
}
The rest of the code also can be simplified.
I would like to retrieve a list with all the Assemblies in an application package in order to load them dynamically.
I'm trying with this code:
public static IEnumerable<Assembly> AssembliesInAppFolder
{
get
{
var entryAssembly = Assembly.GetExecutingAssembly();
var path = entryAssembly.Location;
var folder = Path.GetDirectoryName(path);
var assemblies = new Collection<Assembly>();
var fileNames = FilterFiles(folder, ".dll", ".exe");
foreach (var fileName in fileNames)
{
try
{
assemblies.Add(Assembly.LoadFile(fileName));
}
catch (FileNotFoundException)
{
}
catch (FileLoadException)
{
}
catch (BadImageFormatException)
{
}
}
return assemblies;
}
}
public static IEnumerable<string> FilterFiles(string path, params string[] extensionsWithNoWildcard)
{
return
Directory
.EnumerateFiles(path, "*.*")
.Where(file => extensionsWithNoWildcard.Any(x => file.EndsWith(x, StringComparison.OrdinalIgnoreCase)));
}
But it doesn't work in Android (in Release mode, it throws at Directory.EnumerateFiles(path, "*.*") - where path is an empty string saying this:
"System.ArgumentException: The specified path is not of a legal form (empty)."
I uploaded an image to server using form fileData:
[Route("upload")]
[HttpPost]
public async Task<HttpResponseMessage> Upload()
{
try
{
if (!Request.Content.IsMimeMultipartContent()) {
Request.CreateResponse(HttpStatusCode.UnsupportedMediaType);
}
var provider = GetMultipartProvider();
var result = await Request.Content.ReadAsMultipartAsync(provider);
//Get Album name from Form
var titleOfAlbum = GetTitleOfAlbum(provider);
//get path to file
var pathToCoverDecoded = result.FileData.First().LocalFileName;
//Encodeing to base 64 path
var bytes = Encoding.UTF8.GetBytes(pathToCoverDecoded);
var base64 = Convert.ToBase64String(bytes);
Album al = new Album();
al.Title = titleOfAlbum;
al.PathToCover = base64;
db.Albums.Add(al);
db.SaveChanges();
return new HttpResponseMessage(HttpStatusCode.OK);
}
catch (System.Exception e)
{
return Request.CreateErrorResponse(HttpStatusCode.InternalServerError, e);
}
}
private string GetDesereleazedFileName(MultipartFileData fileData)
{
var fileName = GetFileName(fileData);
return JsonConvert.DeserializeObject(fileName).ToString();
}
private string GetFileName(MultipartFileData fileData)
{
return fileData.Headers.ContentDisposition.FileName;
}
private MultipartFormDataStreamProvider GetMultipartProvider()
{
var uploadFolder = HttpContext.Current.Server.MapPath("~/Files");
if (Directory.Exists(uploadFolder) == false)
{
Directory.CreateDirectory(uploadFolder);
}
return new MultipartFormDataStreamProvider(uploadFolder);
}
private string GetTitleOfAlbum(MultipartFormDataStreamProvider provider)
{
var titleOfAlbum = "";
foreach(var key in provider.FormData.GetValues(0))
{
titleOfAlbum = key;
}
return titleOfAlbum;
}
}
Path looks like:
"C:\Users\Oops\Documents\Visual Studio 2015\Projects\WebApplication1\ForMyCustomers\WebApplication1\Files\BodyPart_b40d80c5-47dc-41db-8e35-9d39d4e27939"
I getting path from FileData:
and convert it to base64, but it doesn't displays at page
I've got File not found error.
How can I resolve it? if the URL is wrong how can I get correct one?
You cannot use physical path (the one you used) on web. The physical path like "C:\something" is the path that can be used only by your OS.
The URL however, is the path that you need and to use and to do that you need to put your files somewhere that is readable by your host (IIS).
You are already writing your files in "~/Files". so you just need to add the file name at the end.
var url= "~/Files/"+filename;
you need to save the file name when you are uploading your file so when you want to fetch data from DB, fetch the file name from DB and create the url using that.
In my app, I am using OneDrive to keep data in sync. I am successfully writing the file to OneDrive, but am having no luck replacing the local outdated data with the newer OneDrive data.
My current method, which completes without throwing an exception, does not return the same text data that the file on OneDrive contains.
Goal of the method is to compare the datemodified to the OneDrive file to the local file, and if OneDrive is newer, write the contents of the OndeDrive file to the local StorageFile, and then return it to be de-serialized.
private async Task<string> GetSavedDataFileAsync(string filename)
{
string filepath = _appFolder + #"\" + KOWGame + #"\" + filename;
StorageFile localread;
BasicProperties localprops = null;
string txt;
try
{
localread = await local.GetFileAsync(filepath);
localprops = await localread.GetBasicPropertiesAsync();
}
catch (FileNotFoundException)
{ localread = null; }
if (_userDrive != null)
{
if (_userDrive.IsAuthenticated)
{
try
{
Item item = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
if (item != null)
{
DateTimeOffset drivemodified = (DateTimeOffset)item.FileSystemInfo.LastModifiedDateTime;
if (localprops != null)
{
if (drivemodified > localprops.DateModified)
{
Stream stream = await localread.OpenStreamForWriteAsync();
using (stream)
{ await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync(); }
}
}
}
}
catch (OneDriveException e)
{
if (e.IsMatch(OneDriveErrorCode.ActivityLimitReached.ToString()))
{ string stop; }
}
}
}
if (localread == null) return string.Empty;
txt = await FileIO.ReadTextAsync(localread);
return txt;
}
I tried to reverse engineer another answer I found on Stack regarding writing a StorageFile to OneDrive, in that I needed to open the stream of the local file, but I doesn't appear to be working properly.
To get the content of a OneDrive item, we need use following method:
var contentStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync();
While using
await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Request().GetAsync();
you are getting the OneDrive Item not its content.
So you can change your code like following to write the content of a Onedrive item to a local file:
if (drivemodified > localprops.DateModified)
{
using (var stream = await localread.OpenStreamForWriteAsync())
{
using (var contentStream = await _userDrive.Drive.Special.AppRoot.ItemWithPath(filepath).Content.Request().GetAsync())
{
contentStream.CopyTo(stream);
}
}
}
I'm trying to make an epub parsing app in a Windows Store with C#, and it won't wait for the archive (epubs are actually zip files) to finish extracting before it tries to parse the not-yet-existing table of contents. How do I make my app be a bit more patient?
I've tried making my UnZip() function return a task and having the epub constructor (epub is a class) use UnZip().Wait(), but that just freezes the app. What do I do?
Edit: Here's my relevant code:
public class epub
{
public string filename;
private StorageFolder unzipFolder;
private IList<epubChapter> _contents;
private bool _parsed = false;
public bool parsed { get { return _parsed; } } //Epub and contents are fully parsed
public epub(string newFilename)
{
_contents = new List<epubChapter>();
filename = newFilename;
UnZipFile().Wait();
getTableOfContents();
}
private async Task UnZipFile()
{
var sourceFolder = Windows.ApplicationModel.Package.Current.InstalledLocation;
StorageFolder localFolder = ApplicationData.Current.LocalFolder;
unzipFolder = await localFolder.CreateFolderAsync(filename, CreationCollisionOption.OpenIfExists);
using (var zipStream = await sourceFolder.OpenStreamForReadAsync(filename))
{
using (MemoryStream zipMemoryStream = new MemoryStream((int)zipStream.Length))
{
await zipStream.CopyToAsync(zipMemoryStream);
using (var archive = new ZipArchive(zipMemoryStream, ZipArchiveMode.Read))
{
foreach (ZipArchiveEntry entry in archive.Entries)
{
if (entry.Name != "")
{
using (Stream fileData = entry.Open())
{
try
{
await unzipFolder.GetFileAsync(entry.Name);
Debug.WriteLine("File at {0} already exists", entry.Name);
continue;
}
catch (FileNotFoundException)
{
Debug.WriteLine("Creating file {0}", entry.Name);
}
StorageFile outputFile = await unzipFolder.CreateFileAsync(entry.Name, CreationCollisionOption.OpenIfExists);
//Debug.WriteLine("Output file created at {0}", outputFile.Path);
using (Stream outputFileStream = await outputFile.OpenStreamForWriteAsync())
{
await fileData.CopyToAsync(outputFileStream);
await outputFileStream.FlushAsync();
}
}
if (entry.Name == "toc.ncx")
{
Debug.WriteLine("toc.ncx found in epub file; parsing it");
getTableOfContents();
}
}
}
}
}
}
}
public void getTableOfContents()
{
string contentsPath = unzipFolder.Path + #"\toc.ncx"; //The file is always called this in valid epubs
try
{
XDocument toc = XDocument.Load(contentsPath);
string nameSpace = getNameSpace(toc);
XElement navMap = firstElementNamed(toc.Root, "navMap");
parseNavPoints(navMap, nameSpace, 0);
_parsed = true;
}
catch(FileNotFoundException)
{
Debug.WriteLine("File toc.ncx was not found!");
}
}
Basically, your question seems to be: How do I call an async method from a constructor?
The short answer is that you don't, instead create an async factory method for your class.
Longer answer: As you noticed, if you call Wait(), your code will block. You can't use await, because constructors can't be async. And if you don't do anything, the constructor is going to return too early.
The solution here is to use an async factory method instead of a constructor. Something like:
private epub(string newFilename)
{
_contents = new List<epubChapter>();
filename = newFilename;
}
public static async Task<epub> CreateAsync(string newFilename)
{
var result = new epub(newFilename);
await result.UnZipFile();
result.getTableOfContents();
return result;
}
For some more information and alternative solutions, see Stephen Cleary's article about async and contructors.