PDF not loading from documents folder on App relaunch iOS - ios

I have a pdf that I download from the server and save it. Next I open the file from the file path within a UIWebView. This works the first time I launch the app. When I relaunch the app again, even thought the file path is the same, the document does not open. Also, the document does exist in the document folder of the app.
I am doing something like :-
SaveToFolder.cs
var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), fileName);
using (FileStream destinationStream = File.Create(filePath))
{
await documentStream.CopyToAsync(destinationStream);
}
File path after saving the document first time :-
/var/mobile/Containers/Data/Application/C3EA2325-81CA-4EC9-8C03-479ACF7EE330/Documents/Insufficiency.pdf
File Path on app relaunch
/var/mobile/Containers/Data/Application/C3EA2325-81CA-4EC9-8C03-479ACF7EE330/Documents/Insufficiency.pdf
Is there something Iam doing wrong?

I have created a file in iOS for reading & writing file. Please have a look in iOS
using System;
using Xamarin.Forms;
using FileReader.iOS;
using System.IO;
using FileReader;
using Foundation;
using System.Linq;
using System.Threading.Tasks;
[assembly: Dependency(typeof(SaveAndLoadiOS))]
namespace FileReader.iOS
{
public class SaveAndLoadiOS : LoadAndSave
{
public static string DocumentPath
{
get
{
var documentURL = NSFileManager.DefaultManager.GetUrls(NSSearchPathDirectory.DocumentDirectory, NSSearchPathDomain.User).Last();
return documentURL.Path;
}
}
public string CreatePath(string fileName)
{
return Path.Combine(DocumentPath, fileName);
}
public async Task SaveTextAsync(string fileName, string text)
{
string path = CreatePath(fileName);
if (IsFileExits(fileName))
{
File.Delete(path);
}
using (StreamWriter sw = File.CreateText(path))
await sw.WriteAsync(text);
}
public async Task<string> LaodTextAsync(string fileName)
{
string path = CreatePath(fileName);
using (StreamReader sr = File.OpenText(path))
return await sr.ReadToEndAsync();
}
public bool IsFileExits(string fileName)
{
return File.Exists (CreatePath(fileName));
}
}
}
For reading from my .CS class (subclass of ContentPage), Below is the code
var tempFileService = DependencyService.Get<LoadAndSave>();
var itemFile = await tempFileService.LaodTextAsync(tempFile.StoredFileName);
var rootobject = JsonConvert.DeserializeObject<Rootobject>(itemFile);
Where LoadAndSave is an interface as below
using System;
using System.Threading.Tasks;
namespace FileReader
{
public interface LoadAndSave
{
Task SaveTextAsync(string fileName, string text);
Task<string> LaodTextAsync(string fileName);
bool IsFileExits(string fileName);
}
}
Hope it helps.

I ran into the same issue a while ago. You can refer Can't find saved file (in device) after restarting the app
According to the answer
You shouldn't store raw file paths for persistence (or if you do, know that the root can move on you). A better practice would be to only store the relative part of the path and always attach it to the current "root" path in question (particularly if you might be sharing data across devices as with iCloud).
Maybe your root is changing as well. You can change your approach and append the filename with the default path to your documents folder like so in Xamarin:-
var docsPath = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
filePath = docsPath +"/" + "Insuffeciency.pdf";
Also, consider changing your Personal folder to MyDocuments folder while saving the file.

Related

Add a Class with byte[] to NSUserDefaults in iOS Share Extension (Xamarin)

In Xamarin.iOS Share Extension, I'd like to store my selected files (like pdf, images, word etc.) in my NSUserDefaults and when the main application starts, I'll have access to the files stored in it.
Here is my file class :
public class MyClass {
public string FileName { get; set; }
public byte[] Content { get; set; }
}
So in LoadItem in DidSelectPost methods, I create by object by using byte[] of files. Then for saving datas in NSUserDefaults, I convert my object to JSON like this (by using Newtonsoft.Json library):
string jsonString = JsonConvert.SerializeObject(myFileObject);
and I set it to NSUserDefaults object like that:
NSUser.SetValueForKey(new NSString(myFileObject), new NSString("FileSharing" + count));
or
NSUser.SetValueForKey(new NSString(myFileObject), new NSString("FileSharing"+ count));
And after I'll synchronise my items : NSUser.Synchronize());
Everything goes well, but when the main application open, the new data is not here.
string toDesralize = nSUser.StringForKey("FileSharing0");
I've tried for many files, and I've found out that this issue works only for files with low height and it doesn't work for files like 14mb. How can i save files with high height in NSUserDefaults? Is there any other way for doing the same job ?
You can use file system to store and read data in Xamarin.iOS:
var documents = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
var filename = Path.Combine(documents, "Write.txt");
//save text
File.WriteAllText(filename, "Write this text into a file");
//save bytes
File.WriteAllBytes(filename,yourBytes);
And the read methods:
var text = File.ReadAllText("TestData/ReadMe.txt");
Console.WriteLine(text);
var bytes = File.ReadAllBytes("path");

How to get Sqlite Connection in Xamarin Forms in iOS?

I've created a Xamarin.Forms PCL project and trying to access the local data stored in sqlite database which is working file in Android but not working in iOS. Whenever I'm trying to call the iOS specific code using DependencyService it throws System.NullReferenceException: Object reference not set to an instance of an object.
Here is my calling statement
var db = DependencyService.Get<IDBPath>().GetDBPath();
Here is my iOS specific code for getting Sqlite Connection
using SQLite.Net;
using SQLite.Net.Async;
using SQLite.Net.Platform.XamarinIOS;
using SwachhParyatanApp.iOS;
using System;
using System.IO;
[assembly: Xamarin.Forms.Dependency(typeof(DBPath_iOS))]
namespace SwachhParyatanApp.iOS
{
class DBPath_iOS
{
public SQLiteAsyncConnection GetDBPath()
{
var sqliteFilename = "localData.db";
string folder = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
string libraryPath = Path.Combine(folder, "..", "Library");
var path = Path.Combine(libraryPath, sqliteFilename);
var platform = new SQLitePlatformIOS();
var param = new SQLiteConnectionString(path, false);
var connection = new SQLiteAsyncConnection(() => new SQLiteConnectionWithLock(platform, param));
return connection;
}
}
}
I don't think the calling method is going to reach the iOS specific code because I used the break point in iOS specific code but it never came to the break point and it immediately gives the error. I've also tried going to the exception for details but there is no inner exception and in stacktrace it only points to the line which called the method.
Using SQLite.Net PCL below is a working example of an iOS dependency injection recipient for SQLite. A couple of differences I noticed are your db extension .db instead of .db3 and your 'assembly' header does not implement the full namespace. I am not sure if that matters.
[assembly: Dependency(typeof(NameSpace.iOS.SQLiteUtility.SQLite_iOS))]
namespace NameSpace.iOS.SQLiteUtility
{
class SQLite_iOS : ISQLite
{
public SQLiteConnection GetConnection()
{
try
{
var sqliteFilename = "MyDB.db3";
string documentsPath = Environment.GetFolderPath(Environment.SpecialFolder.Personal); // Documents folder
string libraryPath = Path.Combine(documentsPath, "..", "Library"); // Library folder
var path = Path.Combine(libraryPath, sqliteFilename);
var plat = new SQLite.Net.Platform.XamarinIOS.SQLitePlatformIOS();
var conn = new SQLite.Net.SQLiteConnection(plat, path,
SQLite.Net.Interop.SQLiteOpenFlags.ReadWrite |
SQLite.Net.Interop.SQLiteOpenFlags.Create |
SQLite.Net.Interop.SQLiteOpenFlags.FullMutex, true);
return conn;
}
catch (SQLiteException ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
catch (System.Exception ex)
{
Helpers.Helper_ErrorHandling.SendErrorToServer(ex);
return null;
}
}
}
If it must be the async version you may want to look at How to use SQLiteAsyncConnection from the async PCL version of SQLite?

Azure: Read files from App_Data [duplicate]

What is the correct way to find the absolute path to the App_Data folder from a Controller in an ASP.NET MVC project? I'd like to be able to temporarily work with an .xml file and I don't want to hardcode the path.
This does not work:
[HandleError]
public class HomeController : Controller
{
public ActionResult Index()
{
string path = VirtualPathUtility.ToAbsolute("~/App_Data/somedata.xml");
//.... do whatever
return View();
}
}
I think outside of the web context VirtualPathUtility.ToAbsolute() doesn't work.
string path comes back as "C:\App_Data\somedata.xml"
Where should I determine the path of the .xml file in an MVC app?
global.asax and stick it an application-level variable?
ASP.NET MVC1 -> MVC3
string path = HttpContext.Current.Server.MapPath("~/App_Data/somedata.xml");
ASP.NET MVC4
string path = Server.MapPath("~/App_Data/somedata.xml");
MSDN Reference:
HttpServerUtility.MapPath Method
string path = AppDomain.CurrentDomain.GetData("DataDirectory").ToString();
This is probably a more "correct" way of getting it.
I try to get in the habit of using HostingEnvironment instead of Server as it works within the context of WCF services too.
HostingEnvironment.MapPath(#"~/App_Data/PriceModels.xml");
The most correct way is to use HttpContext.Current.Server.MapPath("~/App_Data");. This means you can only retrieve the path from a method where the HttpContext is available. It makes sense: the App_Data directory is a web project folder structure [1].
If you need the path to ~/App_Data from a class where you don't have access to the HttpContext you can always inject a provider interface using your IoC container:
public interface IAppDataPathProvider
{
string GetAppDataPath();
}
Implement it using your HttpApplication:
public class AppDataPathProvider : IAppDataPathProvider
{
public string GetAppDataPath()
{
return MyHttpApplication.GetAppDataPath();
}
}
Where MyHttpApplication.GetAppDataPath looks like:
public class MyHttpApplication : HttpApplication
{
// of course you can fetch&store the value at Application_Start
public static string GetAppDataPath()
{
return HttpContext.Current.Server.MapPath("~/App_Data");
}
}
[1] http://msdn.microsoft.com/en-us/library/ex526337%28v=vs.100%29.aspx
Phil Haak has an example that I think is a bit more stable when dealing with paths with crazy "\" style directory separators. It also safely handles path concatenation. It comes for free in System.IO
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Data/uploads"), fileName);
However, you could also try "AppDomain.CurrentDomain.BaseDirector" instead of "Server.MapPath".
string filePath = HttpContext.Current.Server.MapPath("~/folderName/filename.extension");
OR
string filePath = HttpContext.Server.MapPath("~/folderName/filename.extension");
This way i got the hosting path.
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
namespace IHostingEnvironmentExample.Controllers
{
public class HomeController : Controller
{
private IHostingEnvironment _env;
public HomeController(IHostingEnvironment env)
{
_env = env;
}
public IActionResult Index()
{
var webRoot = _env.WebRootPath;
var file = System.IO.Path.Combine(webRoot, "test.txt");
System.IO.File.WriteAllText(file, "Hello World!");
return View();
}
}
}
https://forums.asp.net/t/1696005.aspx?How+to+get+Local+Server+path+in+mvc
string Index = i;
string FileName = "Mutton" + Index + ".xml";
XmlDocument xmlDoc = new XmlDocument();
var path = Path.Combine(Server.MapPath("~/Content/FilesXML"), FileName);
xmlDoc.Load(path); // Can use xmlDoc.LoadXml(YourString);
this is the best Solution to get the path what is exactly need for now

Customize stream playback paths in Red5 to access file from a shared machine

I have installed Red5 server. I have created a custom application same as oflaDemo. I am able to play videos in /streams folder of my application, my application name is demo.I want to change the directory RED5_HOME/demo/webapps/streams from which my application is accessing videos, to a folder in a shared machine. I am able to change to a directory in local machine, for example "c:\streams". I have achieved this using CustomFileNameGenerator implementing IStreamFilenameGenerator . But I am not able to access a shared folder. Here is my CustomFileNameGenerator class
import java.io.File;
import org.apache.log4j.Logger;
import org.red5.server.api.scope.IScope;
import org.red5.server.api.stream.IStreamFilenameGenerator;
public class CustomFilenameGenerator implements IStreamFilenameGenerator {
Logger log = Logger.getLogger(CustomFilenameGenerator.class);
/** Path that will store recorded videos. */
/public String recordPath = "recordedStreams/";/
/** Path that contains VOD streams. */
public String playbackPath;
/** Set if the path is absolute or relative */
public Boolean resolvesAbsolutePath;
public String generateFilename(IScope scope, String name, GenerationType type) {
// Generate filename without an extension.
return generateFilename(scope, name, null, type);
}
public String generateFilename(IScope scope, String name, String extension, GenerationType type) {
String filename = null;
if (type == GenerationType.PLAYBACK)
{
filename = playbackPath + name;
}
log.info("file Name " + filename);
if (extension != null)
// Add extension
filename += extension;
log.info("Extension and file name " + filename);
return filename;
}
public boolean resolvesToAbsolutePath()
{
log.info("resolvesAbsolutePath" + resolvesAbsolutePath);
return resolvesAbsolutePath;
}
public void setPlaybackPath(String playbackPath) {
this.playbackPath = playbackPath;
}
public void setResolvesAbsolutePath(Boolean resolvesAbsolutePath) {
this.resolvesAbsolutePath = resolvesAbsolutePath;
}
}
Following are properties in my red5-web.properties file:
webapp.contextPath=/demo
webapp.virtualHosts=*, 192.168.1.20, 192.168.1.20:8088, 127.0.0.1:8088, 192.168.1.20:1935
playbackPath=C://streams/
resolvesAbsolutePath=true
Following is the bean definition in my red5-web.xml file
<bean id="streamFilenameGenerator" class="com.abhinow.demo.CustomFilenameGenerator" >
<property name="playbackPath" value="${playbackPath}" />
<property name="resolvesAbsolutePath" value="${resolvesAbsolutePath}" />
</bean>
The above given code is working fine and I am able to playback video in C:\streams folder, but when I have changed the playback path to a shared folder like
/192.168.1.20/streams
it is not working. I am using windows computer. I have also tried by mapping shared folder /192.168.1.20/streams to a network drive using map network drive feature in windows and gave name to that drive as Z:. Then I have tried by giving the path
Z://streams
Now also it is not working.
Any one please help where I am getting it wrong. I have been struggling on it for two days. Please help me to solve this issue.
Thanks a lot in advance.
Did you debug if
generateFilename
is ever called in your application?
The first thing might be to add some log.debug statements to the code and having a look at the red5.log (or std.out).
Sebastian
Try:
public String generateFilename(IScope scope, String name, String extension, GenerationType type) {
...
return playbackPath+filename;
}

Relative path on File.ReadAllLines method

My code access a file which is in "Conf" directory inside my project directory. I am currently opening the file using absolute path like below:
File.ReadAllLines("C:\project name\Conf\filename");
I was thinikng if it's possible to use the relative path like
File.ReadAllLines("/Conf/filename");
But it's not working; as expected it throws exception. I did checked MSDN (link below) but seems "ReadAllLines()" methods doesn't accept relative path.
http://msdn.microsoft.com/en-us/library/s2tte0y1.aspx
Any idea, how can I use the relative path instead using absolute path?
Thanks,
Rahul
This is my favorite way of doing it.
Make your file an embedded resource.
/// <summary>
/// This class must be in the same folder as the embedded resource
/// </summary>
public class GetResources
{
private static readonly Type _type = typeof(GetResources);
public static string Get(string fileName)
{
using (var stream =
_type.Assembly.GetManifestResourceStream
(_type.Namespace + "." + fileName))
{
if (stream != null)
using (var reader = new StreamReader(stream))
{
return reader.ReadToEnd();
}
}
throw new FileNotFoundException(fileName);
}
}
As stated in MSDN you cannot use a relative path, however you might be able to use either Environment.CurrentDirectory or System.Reflection.Assembly.GetExecutingAssembly().Location
To make things simple, use the following:
string current_path = System.IO.Path.GetDirectoryName(Application.ExecutablePath);
string[] lines_from_file = System.IO.File.ReadAllLines(current_path + "/Conf/filename");
...additional black magic here...

Resources