Download offline version of images and display in xamarin forms - forms

in my app I would like to program an "offline version" of an display. That is, you can decide in advance to download the images and later videos and then view them without mobile data (due to network coverage), for example.
I use step 1 (Xamarin.Forms: How to download an Image, save it locally and display it on screen?) to download the file.
Step 2 I can not use because the maximum data size is exceeded for larger files. So I use PCLStorage and save the file locally.
IFolder folder = FileSystem.Current.LocalStorage;
IFile file = await folder.CreateFileAsync(imageFileName, CreationCollisionOption.ReplaceExisting);
await file.WriteAllTextAsync(Convert.ToBase64String(imageAsBase64String));
Now I want to display the images again, but no matter what I do, the image is not shown.
I rewrite the imagesource URL to the local file. No matter if only with "filename.jpg" or "file:///data/user/0/com.xxx.yyy_app/files/filename.jpg" no image is shown.
But I can't even check if the image was downloaded correctly as such, because the files in localstorage are not shown in a filemanager. folder.CheckExistsAsync says that the file exists.
Partly I get this error:
[0:] Image Loading: Error getting stream for file:///data/user/0/com.xxx.yyy_app/files/2_76_1.jpg: System.InvalidOperationException:
An invalid request URI was provided. The request URI must either be an absolute URI or BaseAddress must be set.
at System.Net.Http.HttpClient.PrepareRequestMessage (System.Net.Http.HttpRequestMessage request) [0x0008a] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:678
at System.Net.Http.HttpClient.SendAsync (System.Net.Http.HttpRequestMessage request, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x00020] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:437
at System.Net.Http.HttpClient.GetAsync (System.Uri requestUri, System.Net.Http.HttpCompletionOption completionOption, System.Threading.CancellationToken cancellationToken) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:311
at System.Net.Http.HttpClient.GetAsync (System.Uri requestUri, System.Threading.CancellationToken cancellationToken) [0x00000] in /Users/builder/jenkins/workspace/archive-mono/2020-02/android/release/external/corefx/src/System.Net.Http/src/System/Net/Http/HttpClient.cs:299
at Xamarin.Forms.StreamWrapper.GetStreamAsync (System.Uri uri, System.Threading.CancellationToken cancellationToken, System.Net.Http.HttpClient client) [0x00011] in D:\a\1\s\Xamarin.Forms.Core\StreamWrapper.cs:99
at Xamarin.Forms.Forms+AndroidPlatformServices.GetStreamAsync (System.Uri uri, System.Threading.CancellationToken cancellationToken) [0x0003f] in D:\a\1\s\Xamarin.Forms.Platform.Android\Forms.cs:854
at Xamarin.Forms.UriImageSource.GetStreamAsync (System.Uri uri, System.Threading.CancellationToken cancellationToken) [0x000cf] in D:\a\1\s\Xamarin.Forms.Core\UriImageSource.cs:136
Long story short: I want to download an image and display it from the local file system. How do I do that?
Thanks a lot for the help.
Translated with www.DeepL.com/Translator (free version)

I want to download an image and display it from the local file system. How do I do that?
The correct steps
Download Image from remote url .
Save it into disk locally .
Get the steam data via the path(folder+fileName).
Display it on Image .
This solution works well but in your scenario the difference that the image size is too big , so we just need to replace Xamarin.Essentials.Preferences with built-in File Handling, cause the later one can handle large file storage .
Modify the method SaveToDisk and GetFromDisk .
string folder = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
public static void SaveToDisk(string imageFileName, byte[] imageAsBase64String)
{
var fileName = Path.Combine(folder ,imageFileName);
File.WriteAllText(fileName , Convert.ToBase64String(imageAsBase64String));
}
public static Xamarin.Forms.ImageSource GetFromDisk(string imageFileName)
{
var fileName = Path.Combine(folder ,imageFileName);
var imageAsBase64String = File.ReadAllText(fileName)
return ImageSource.FromStream(() => new MemoryStream(Convert.FromBase64String(imageAsBase64String)));
}

Related

What is the difference between blobtargetoption and blobwriteoption

I was practicing some google cloud upload, while creating the blob I see two options to set BlobWriteOption.predefinedAcl(Storage.PredefinedAcl acl) and BlobTargetOption.predefinedAcl(Storage.PredefinedAcl acl)
I'm curious to know the difference and went through:
https://googleapis.dev/java/google-cloud-clients/latest/com/google/cloud/storage/Bucket.BlobTargetOption.html
and
https://dev.ionic.com/sdk_docs/ionic_gcs_sdk/java/version_0.9.1/com/google/cloud/storage/Storage.BlobWriteOption.html
but couldn't get much difference between two.
I couldn’t find much, but it seems like the BlobWriteOption is used when input streams are involved.
For instance on this doc both methods with streams are using BlobWriteOption
public Blob create(String blob,
InputStream content,
String contentType,
Bucket.BlobWriteOption... options)
public Blob create(String blob,
InputStream content,
Bucket.BlobWriteOption... options)
As opposed to BlobTargetOption don’t have input streams
public Blob create(String blob,
byte[] content,
String contentType,
Bucket.BlobTargetOption... options)
public Blob create(String blob,
byte[] content,
Bucket.BlobTargetOption... options)
It looks like this pattern repeats on these docs as well:
https://jar-download.com/artifacts/com.google.cloud/google-cloud-storage/1.14.0/source-code/com/google/cloud/storage/Bucket.java
https://github.com/googleapis/google-cloud-java/blob/master/google-cloud-clients/google-cloud-storage/src/test/java/com/google/cloud/storage/BucketTest.java

Big Delay in ImapClient, SmtpClient and Pop3Client Connect method when using secure connection

I notice up to 13 sec delay when trying to connect SMTP or IMAP client from MailKit. It seems the delay happens on the call to AuthenticateAsClient method inside MailKit library. I found explanation for the problem here https://blogs.msdn.microsoft.com/alejacma/2011/09/27/big-delay-when-calling-sslstream-authenticateasclient/
This is not an issue in MailKit but I want to know the best workaround for this problem using MailKit. It seems cancellationToken passed to MailKit Connect method is not cancelling this method call. This is a problem when you want to shutdown your process while SmtpClient is connecting, because you want to wait the dedicated thread that runs this code to complete.
This is the workaround I have but it is not very elegant
public override void Connect(string host, int port = 0, SecureSocketOptions options = SecureSocketOptions.Auto, CancellationToken cancellationToken = default(CancellationToken))
{
var connectionTask = smtpClient.ConnectAsync(host, port, options, cancellationToken);
Task.WhenAny(connectionTask, cancellationToken.WhenCanceled()).GetAwaiter().GetResult();
cancellationToken.ThrowIfCancellationRequested();
}
public static class Extensions
{
public static Task WhenCanceled(this CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
cancellationToken.Register(s => ((TaskCompletionSource<bool>)s).SetResult(true), tcs);
return tcs.Task;
}
}
This above will work most of the time but if cancel the token the connection will stay in connecting state (because AuthenticateAsClient is not cancellable) and the user might attempt to execute another command while the smtpClient is still trying to connect.
I am looking for better approach for cancelling the Connect method in the context of MailKit.

How to close InputStream which fed into Response(jax.rs)

#GET
#Path("/{id}/content")
#Produces({ "application/octet-stream" })
public Response getDocumentContentById(#PathParam("id") String docId) {
InputStream is = getDocumentStream(); // some method which gives stream
ResponseBuilder responseBuilder = Response.ok(is);
responseBuilder.header("Content-Disposition", "attachment; filename=" + fileName);
return responseBuilder.build();
}
Here how can I close the InputStream is ? If something(jax.rs) closes automatically. Please give me some information. Thank you.
When you're wanting to stream a custom response, the most reliable way I've found is to return an object that contains the InputStream (or which can obtain the stream in some other way at some point), and to define a MessageBodyWriter provider that will do the actual streaming at the right time.
For example, this code is part of Apache Taverna, and it streams back the zipped contents of a directory. All that the main code needs to do to use it is to return a ZipStream as the response (which can be packaged in a Response or not) and to ensure that it is dealing with returning the application/zip content type. The final point to note is that since this is dealing with CXF, you need to manually register the provider; unlike with Glassfish, they are not automatically picked up. This is a good thing in sophisticated scenarios, but it does mean that you need to do the registration.

Convert a file being required to include it's mimetype in the inkblob

I'm using File Picker for handling files for my web application. In my front end app I have the URL to the file's handle (ex. https://www.filepicker.io/api/file/H7KYuWy1S3e1qvG2M66i), but I don't have it's complete inkBlob.
The file is an image and I want to do a convert operation on this file. It seems that I am required to include the mimetype when calling on the convert function.
Taken from File Picker's API documentation on convert, this works:
var inkblob = {
url: 'https://www.filepicker.io/api/file/H7KYuWy1S3e1qvG2M66i',
filename: 'customers.jpg', mimetype: 'image/jpeg',
isWriteable: false, size: 629454
};
var result = document.getElementById("convert-result");
filepicker.convert(inkblob, {width: 200, height: 200},
function(new_InkBlob){
console.log(new_InkBlob.url);
result.src = new_InkBlob.url;
}
);
The same code works with an inkblob like this:
var inkblob = {
url: 'https://www.filepicker.io/api/file/H7KYuWy1S3e1qvG2M66i',
mimetype: 'image/jpeg',
};
However, the convert does not work if you exclude the mimetype and only include the url.
In my situation it requires me to first do a stat call against the filehandle to retrieve the mimetype from File Picker's API, then send the mimetype right back to filepicker when I do the convert command.
Is it possible to make it so the mimetype is omitted and File Picker looks the mimetype up internally if it isn't included in the convert command?
For convert() method, mimetype is just a security check to ensure you pass an image. If you pass 'image/jpeg' even if it's not the correct mimetype for the image, it should work just fine. So no need to stat file every time.
For other people wanting answer to this question, here are some more information we communicated on mail:
The answer they gave on mail:
For convert() method, mimetype is just a security check to ensure you
pass an image. If you pass 'image/jpeg' even if it's not the correct
mimetype for the image, it should work just fine. So no need to stat
file every time.
My answer back:
Hi, and thanks for your answer.
It sounds to me like the inclusion of mimetype when calling on the
convert() function is redundant. You tell me I should be able to
simply pass in a fixed mimetype thus I cannot see how this is a useful
security check. If you want to ensure the convert command is executed
on an image File Picker already knows which mimetype a file has and I
believe this should be handled internally when the convert() command
is executed not trusting client's claim of a mimetype on a file.
I haven't tried this; but what happens if I do a convert command with
mimetype set to 'image/jpeg' on a text file? I guess it will fail
which is another reason for me thinking this mimetype requirement on
convert command is obsolete.
Answer from filepicker:
This is really just basic protection from accidental passing files
other than image to the convert call. I'm aware this can be redundant
for you but it works in 95% of cases and we never had issue with this.

Help with a Windows Service/Scheduled Task that must use a web browser and file dialogs

What I'm Trying To Do
I'm trying to create a solution of any kind that will run nightly on a Windows server, authenticate to a website, check a web page on the site for new links indicating a new version of a zip file, use new links (if present) to download a zip file, unzip the downloaded file to an existing folder on the server, use the unzipped contents (sql scripts, etc.) to build an instance of a database, and log everything that happens to a text file.
Forms App: The Part That Sorta Works
I created a Windows Forms app that uses a couple of WebBrowser controls, a couple of threads, and a few timers to do all that except the running nightly. It works great as a Form when I'm logged in and run it, but I need to get it (or something like it) to run on it's own like a Service or scheduled task.
My Service Attempt
So, I created a Windows Service that ticks every hour and, if the System.DateTime.Now.Hour >= 22, attempts to launch the Windows Forms app to do it's thing. When the Service attempts to launch the Form, this error occurs:
ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded apartment.
which I researched and tried to resolve by either placing the [STAThread] attribute on the Main method of the Service's Program class or using some code like this in a few places including the Form constructor:
webBrowseThread = new Thread(new ThreadStart(InitializeComponent));
webBrowseThread.SetApartmentState(ApartmentState.STA);
webBrowseThread.Start();
I couldn't get either approach to work. In the latter approach, the controls on the Form (which would get initialized inside IntializeComponent) don't get initialized and I get null reference exceptions.
My Scheduled Task Attempt
So, I tried creating a nightly scheduled task using my own credentials to run the Form locally on my dev machine (just testing). It gets farther than the Service did, but gets hung up at the File Download Dialog.
Related Note: To send the key sequences to get through the File Download and File Save As dialogs, my Form actually runs a couple of vbscript files that use WScript.Shell.SendKeys. Ok, that's embarassing to admit, but I tried a few different things including SendMessage in Win32 API and referencing IWshRuntimeLibrary to use SendKeys inside my C# code. When I was researching how to get through the dialogs, the Win32 API seemed to be the recommended way to go, but I couldn't figure it out. The vbscript files was the only thing I could get to work, but I'm worried now that this may be the reason why a scheduled task won't work.
Regarding My Choice of WebBrowser Control
I have read about the System.WebClient class as an alternative to the WebBrowser control, but at a glance, it doesn't look like it has what I need to get this done. For example, I needed (or I think I needed) the WebBrowser's DocumentCompleted and FileDownload events to handle the delays in pages loading, files downloading, etc. Is there more to WebClient that I'm not seeing? Is there another class besides WebBrowser that is more Service-friendly and would do the trick?
In Summary
Geez, this is long. Sorry! It would help to even have a high level recommendation for a better way to do what I'm trying to do, because nothing I've tried has worked.
Update 10/22/09
Well, I think I'm closer, but I'm stuck again. I should end up with a decent-sized zip file with several files in it, but the zip file resulting from my code is empty. Here's my code:
// build post request
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(targetHref);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
// encoding to use
Encoding enc = Encoding.GetEncoding(1252);
// build post string containing authentication information and add to post request
string poststring = "returnUrl=" + fixCharacters(targetDownloadFileUrl);
poststring += getUsernameAndPasswordString();
poststring += "&login2.x=0&login2.y=0";
// convert to required byte array
byte[] postBytes = enc.GetBytes(poststring);
request.ContentLength = postBytes.Length;
// write post to request
Stream postStream = request.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length);
postStream.Close();
// get response as stream
HttpWebResponse response = (HttpWebResponse)request.GetResponse();
Stream responseStream = response.GetResponseStream();
// writes stream to zip file
FileStream writeStream = new FileStream(fullZipFileName, FileMode.Create, FileAccess.Write);
ReadWriteStream(responseStream, writeStream);
response.Close();
responseStream.Close();
The code for ReadWriteStream looks like this.
private void ReadWriteStream(Stream readStream, Stream writeStream)
{
// taken verbatum from http://www.developerfusion.com/code/4669/save-a-stream-to-a-file/
int Length = 256;
Byte[] buffer = new Byte[Length];
int bytesRead = readStream.Read(buffer, 0, Length);
// write the required bytes
while (bytesRead > 0)
{
writeStream.Write(buffer, 0, bytesRead);
bytesRead = readStream.Read(buffer, 0, Length);
}
readStream.Close();
writeStream.Close();
}
The building of the post string is taken from my previous forms app that works. I compared the resulting values in poststring for both sets of code (my working forms app and this one) and they're identical.
I'm not even sure how to troubleshoot this further. Anyone see anything obvious as to why this isn't working?
Conclusion 10/23/09
I finally have this working. A couple of important hurdles I had to get over. I had some problems with the ReadWriteStream method code that I got online. I don't know why, but it wasn't working for me. A guy named JB in Claudio Lassala's Virtual Brown Bag meeting helped me to come up with this code which worked much better for my purposes:
private void WriteResponseStreamToFile(Stream responseStreamToRead, string zipFileFullName)
{
// responseStreamToRead will contain a zip file, write it to a file in
// the target location at zipFileFullName
FileStream fileStreamToWrite = new FileStream(zipFileFullName, FileMode.Create);
int readByte = responseStreamToRead.ReadByte();
while (readByte != -1)
{
fileStreamToWrite.WriteByte((byte)readByte);
readByte = responseStreamToRead.ReadByte();
}
fileStreamToWrite.Flush();
fileStreamToWrite.Close();
}
As Will suggested below, I did have trouble with the authentication. The following code is what worked to get around that issue. A few comments inserted addressing key issues I ran into.
string targetHref = "http://wwwcf.nlm.nih.gov/umlslicense/kss/login.cfm";
HttpWebRequest firstRequest = (HttpWebRequest)WebRequest.Create(targetHref);
firstRequest.AllowAutoRedirect = false; // this is critical, without this, NLM redirects and the whole thing breaks
// firstRequest.Proxy = new WebProxy("127.0.0.1", 8888); // not needed for production, but this helped in order to debug the http traffic using Fiddler
firstRequest.Method = "POST";
firstRequest.ContentType = "application/x-www-form-urlencoded";
// build post string containing authentication information and add to post request
StringBuilder poststring = new StringBuilder("returnUrl=" + fixCharacters(targetDownloadFileUrl));
poststring.Append(getUsernameAndPasswordString());
poststring.Append("&login2.x=0&login2.y=0");
// convert to required byte array
byte[] postBytes = Encoding.UTF8.GetBytes(poststring.ToString());
firstRequest.ContentLength = postBytes.Length;
// write post to request
Stream postStream = firstRequest.GetRequestStream();
postStream.Write(postBytes, 0, postBytes.Length); // Fiddler shows that post and response happen on this line
postStream.Close();
// get response as stream
HttpWebResponse firstResponse = (HttpWebResponse)firstRequest.GetResponse();
// create new request for new location and cookies
HttpWebRequest secondRequest = (HttpWebRequest)WebRequest.Create(firstResponse.GetResponseHeader("location"));
secondRequest.AllowAutoRedirect = false;
secondRequest.Headers.Add(HttpRequestHeader.Cookie, firstResponse.GetResponseHeader("Set-Cookie"));
// get response to second request
HttpWebResponse secondResponse = (HttpWebResponse)secondRequest.GetResponse();
// write stream to zip file
Stream responseStreamToRead = secondResponse.GetResponseStream();
WriteResponseStreamToFile(responseStreamToRead, fullZipFileName);
responseStreamToRead.Close();
sl.logScriptActivity("Downloading update.");
firstResponse.Close();
I want to underscore that setting AllowAutoRedirect to false on the first HttpWebRequest instance was critical to the whole thing working. Fiddler showed two additional requests that occurred when this was not set, and it broke the rest of the script.
You're trying to use UI controls to do something in a windows service. This will never work.
What you need to do is just use the WebRequest and WebResponse classes to download the contents of the webpage.
var request = WebRequest.Create("http://www.google.com");
var response = request.GetResponse();
var stream = response.GetResponseStream();
You can dump the contents of the stream, parse the text looking for updates, and then construct a new request for the URL of the file you want to download. That response stream will then have the file, which you can dump on the filesystem and etc etc.
Before you wonder, GetResponse will block until the response returns, and the stream will block as data is being received, so you don't need to worry about events firing when everything has been downloaded.
You definitely need to re-think your approach (as you've already begun to do) to eliminate the Forms-based application approach. The service you're describing needs to operate with no UI at all.
I'm not familiar with the details of System.WebClient, but since it
provides common methods for sending
data to and receiving data from a
resource identified by a URI,
it will probably be your answer.
At first glance, WebClient.DownloadFile(...) or WebClient.DownloadFileAsync(...) will do what you need.
The only thing I can add is that once you have scraped your screen and have the fully qualified name of the file you want to download, you could pass it along to the Windows/DOS command 'get' which will fetch files via HTTP. You can also script a command-line FTP client if desired. It's been a long time since I tried something like this in Windows, but I think you're almost there. Once you have fetched the correct file, building a batch file to do everything else should be pretty easy. If you are more comfortable with Unix, google "unix services for windows" just keep an eye on the services they start running (DHCP, etc). There are some nice utilities which will let your treat dos as a unix-like shell (ls -l, grep, etc) Finally, you could try another language like Perl or Python but I don't think that's the kind of advice you were looking for. :)