Xamarin.Forms: DirectoryNoFoundException on WritingBytes - xamarin.forms

i download a video from a server as bytes and write those bytes as mp4 to the phones harddrive in pcl:
byte[] stream = await VideoAPI.DownloadVideo(AdID);
File.WriteAllBytes("/storage/emulated/0/Android/data/com.companyname.interiorcircle/files/Movies/file.mp4", stream);
But this returns the following error:
(stream is not null)
{System.IO.DirectoryNotFoundException: Could not find a part of the path "/storage/emulated/0/Android/data/com.companyname.interiorcircle/files/Movies/file.mp4".
But that doesnt make sense, since I am writing the file and path.
Where is this error comming from?
(Wiredly enough, while testing this worked once, but then not again)
Thank you!

use the Environment.SpecialFolder to create a path, not a string variable.
byte[] stream = await VideoAPI.DownloadVideo(AdID);
string fullPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "file.mp4"); //also you can create extra folders
File.WriteAllBytes(fullPath , stream);
also, you can use different folders such as;
Environment.SpecialFolder.MyVideos
Environment.SpecialFolder.Personel
Environment.SpecialFolder.ApplicationData
Environment.SpecialFolder.Templates
Environment.SpecialFolder.CommonApplicationData
Environment.SpecialFolder.MyMusic
Environment.SpecialFolder.MyPictures
Environment.SpecialFolder.Fonts
Environment.SpecialFolder.MyDocuments
Environment.SpecialFolder.Desktop

Related

Stream on the fly zipped files to client via rest endpoint

I am trying to stream on the fly zipped files but memory consumption is high. For example, to zip total file size of 2.8 GB is taking nearly 5 GB of processor memory.
[Route("zip")]
public class ZipController : ControllerBase
{
private readonly HttpClient _httpClient;
public ZipController()
{
_httpClient = new HttpClient();
}
[HttpPost]
public async Task Zip([FromBody] JsonToZipInput input)
{
Response.ContentType = "application/octet-stream";
Response.Headers.Add($"Content-Disposition", $"attachment; filename=\"{input.FileName}\"");
using var zipArchive =
new ZipArchive(Response.BodyWriter.AsStream(), ZipArchiveMode.Create);
foreach (var (key, value) in input.FilePathsToUrls)
{
var zipEntry = zipArchive.CreateEntry(key, CompressionLevel.Optimal);
await using var zipStream = zipEntry.Open();
await using var stream = await _httpClient.GetStreamAsync(value);
await stream.CopyToAsync(zipStream);
}
}
}
I believe you should be able to call Response.StartAsync:
[HttpPost]
public async Task Zip([FromBody] JsonToZipInput input)
{
Response.ContentType = "application/octet-stream";
Response.Headers.Add($"Content-Disposition", $"attachment; filename=\"{input.FileName}\"");
await Response.StartAsync();
using var zipArchive = new ZipArchive(Response.BodyWriter.AsStream(), ZipArchiveMode.Create);
foreach (var (key, value) in input.FilePathsToUrls)
{
var zipEntry = zipArchive.CreateEntry(key, CompressionLevel.Optimal);
await using var zipStream = zipEntry.Open();
await using var stream = await _httpClient.GetStreamAsync(value);
await stream.CopyToAsync(zipStream);
}
}
StartAsync should start the response being sent. Note that neither the response headers nor the status code can be modified once StartAsync is called.
In particular, this means that your exception handling will be different. Previously, an exception (e.g., from a bad URL in the request) would cause an exceptional status code (i.e., 500). With a streaming response, any exceptions after StartAsync cannot change the status code; it's already been sent. Instead, it will appear to the client as though the connection was terminated without a clean close. Complicating this a bit further, this behavior is not uncommon for web servers to do in the successful case, so clients may not complain - they would just end up with truncated (invalid) zip files. (In the case of streaming zips, the "file table" in the zip is sent last instead of first).
So, this should work, but I also recommend:
Ensure your exception logging works for exceptions after StartAsync. There is no way to return error details to the client, so you must rely on logging.
If you control the client, test out this new error situation, and see if you can detect it. If it's not detectable using that client, then ensure your code validates the zip.
Nothing about the zip file format should require a large amount of memory for this use case. It's essential all the files in order, with a table at the end describing the zip structure, and file offsets. This makes it possible to stream very efficiently without using much memory at all.
You may not need to write this yourself, ZipStreamer is a micro service you host that does exactly this (disclosure, I'm the author). It's designed to solve the exact problems you are hitting by streaming the bytes out as soon as they come in, with a fixed buffer size to prevent blowing up memory. It can stream hundreds of zips files in parallel using only a few MB of memory.
If you need this to be part of your application, here are some suggestions.
Disable compression will save CPU, and a bit of memory. Depending on your files, compression might not be a major benefit (jpegs actually get bigger after zip compression). If you're zipping just to combine many files into one, this will really help. But this doesn't explain using GB of memory.
Ensure you're not holding the stream content any longer than you need to, it looks like you are. Start streaming back asap as #Stephen suggested with StartAsync.

Get length of data from inputstream opened by content-resolver

I am doing video (and also photo) uploading to the server by using HttpURLConnection.
I have an Uri of a video. I open an InputStream this way:
InputStream inputStream = context.getContentResolver().openInputStream(uri);
As video file is pretty big, I can't buffer data while writing it into the outputStream. So I need to use setFixedLengthStreamingMode(contentLength) method of HttpURLConnection. But it requires "contentLength".
The question is, how to get the length of the video?
Please don't suggest getting filepath. On some devices it works, but it often fails (especially on Android 6). They say Uri doesn't necessarily represent a file.
I also stumbled onto situations when after opening device gallery (with Intent) I receive an Uri of a picture, but I fail trying to get filepath from it. So I believe it's not a good way to get filepath from Uri?
Try something like this:
void uploadVideo() {
InputStream inputStream = context.getContentResolver().openInputStream(uri);
// Your connection.
HttpURLConnection connection;
// Do connection setup, setDoOutput etc.
// Be sure that the server is able to handle
// chunked transfer encoding.
connection.setChunkedStreamingMode(0);
OutputStream connectionOs = connection.getOutputStream();
// Read and write a 4 KiB chunk a time.
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = inputStream.read(buffer)) != -1) {
connectionOs.write(buffer, 0, bytesRead);
}
// Close streams, do connection etc.
}
UPDATE: added setChunkedStreamingMode

Is there a cross-platform solution to ImageSource to byte[]?

I did researches and fell on this solution: http://forums.xamarin.com/discussion/22682/is-there-a-way-to-turn-an-imagesource-into-a-byte-array
Initial question: http://forums.xamarin.com/discussion/29569/is-there-a-cross-platform-solution-to-imagesource-to-byte#latest
We want to upload an image through a HTTP Post, here's what we tried:
HttpClient httpClient = new HttpClient ();
byte[] TargetImageByte = **TargetImageSource**; //How to convert it to a byte[]?
HttpContent httpContent = new ByteArrayContent (TargetImageByte);
httpClient.PostAsync ("https://api.magikweb.ca/debug/file.php", httpContent);
We also are having a hard time with the libraries we gotta include in the using clauses. It seems like using System.IO; works, but it doesn't give us access to classes like FileInfo or FileStream.
Anybody has any idea how this can be done aside from custom platform-specific converters?
Possibly a Xamarin.Forms.ImageSource function toByte()?
Lemme know if you need more information.
TargetImageSource is a Xamarin.Forms.ImageSource.
ImageSource TargetImageSource = null;
Solution (Sten was right)
The ImageSource has to originate from another type to exist, that previous type can be converted to a byte[]. In this case, I use the Xamarin.Forms.Labs to take a picture and it returns a MediaFile in which a FileStream is accessible through the Source property.
//--Upload image
//Initialization
HttpClient httpClient = new HttpClient ();
MultipartFormDataContent formContent = new MultipartFormDataContent ();
//Convert the Stream into byte[]
byte[] TargetImageByte = ReadFully(mediaFile.Source);
HttpContent httpContent = new ByteArrayContent (TargetImageByte);
formContent.Add (httpContent, "image", "image.jpg");
//Send it!
await httpClient.PostAsync ("https://api.magikweb.ca/xxx.php", formContent);
App.RootPage.NavigateTo (new ClaimHistoryPage());
The function:
public static byte[] ReadFully(Stream input)
{
using (MemoryStream ms = new MemoryStream()){
input.CopyTo(ms);
return ms.ToArray();
}
}
I think you're looking at it a bit backwards.
ImageSource is a way to provide a source image for Xamarin.Forms.Image to show some content. If you're already showing something on the screen your Image view was populated with data that came from elsewhere, such as a file or resource or stored in an array in memory... or however else you got that in the first place. Instead of trying to get that data back from ImageSource you can keep a reference to it and upload it as needed.
Maybe you can elaborate a bit on your particular need if you don't feel this solution applies to your case.
Pseudo code:
ShowImage(){
ImageSource imageSource = ImageSource.FromFile("image.png"); // read an image file
xf_Image.Source = imageSource; // show it in your UI
}
UploadImage(){
byte[] data = File.ReadAll("image.png");
// rather than
// byte[] data = SomeMagicalMethod(xf_Image.Source);
HttpClient.Post(url, data);
}
UPDATE:
Since you're taking a picture you can copy the MediaFile.Source stream into a memory stream, then you can reset the memory stream's position to point at the beginning of the stream so that you can read it once again and copy it to the http body.
Alternatively you can store the MediaFile.Source to a file and use ImageSource.FromFile to load it in the UI, and when necessary - you can copy the file's contents into an http post body.

Video Playing From Memory Stream

I am working in asp.net c#. I want to play video from memory stream. I am encrypting and decrypting video. I am storing the decrypted video in memory stream, and want to play it, without saving. I have googled it and found number of post, but mostly the post are uncompleted or provided the link with directshow. I have also tried with directshow, but it's totally new for me and contains number of demos, that made a confusion which one to use for Memory stream.
I just want to play decrypted video data from memory stream . Please let me know what I can do, it will be more good if there is a sample available from any forums.
My decrypted code
public bool DecryptData(String inName, String outName, byte[] rijnKey, byte[] rijnIV)
{
FileStream fin = null;
FileStream fout = null;
CryptoStream decStream = null;
try
{
fin = new FileStream(inName, FileMode.Open, FileAccess.Read);
//Create variables to help with read and write.
byte[] bin = new byte[bufLen]; //This is intermediate storage for the encryption.
long rdlen = 0; //This is the total number of bytes written.
long totlen = fin.Length; //This is the total length of the input file.
int len; //This is the number of bytes to be written at a time.
RijndaelManaged rijn = new RijndaelManaged();
//DES ds = new DESCryptoServiceProvider();
decStream = new CryptoStream(fin, rijn.CreateDecryptor(rijnKey, rijnIV), CryptoStreamMode.Read);
//odkoduj testowy fragment
byte[] test = new byte[testHeader.Length];
decStream.Read(test, 0, testHeader.Length);
string contents = new StreamReader(decStream).ReadToEnd();
byte[] unicodes = Encoding.Unicode.GetBytes(contents);
MemoryStream msOutput = new MemoryStream(unicodes);
//here I have to implement player that plays from memory stream.
}
catch
{}
}
I have answered one question regarding encrypting and decryption of a video file but i can understand you don't want to save a physical copy of that file on client machine.
https://stackoverflow.com/a/58129727/9869635
But it is not possible to play a video file from memorystream (not sure about some paid third party tools)
so one way you can do it like below approach:
1: Save that file in client's "temp" folder e.g. "temp/myvideos/sample.mkv"
2: Make it hidden from properties (How to hide file in C#?)
3: Play the video from there
4: once it is played, delete all files from that custom folder from "temp" folder (myvideos).
The best way to do it today, that works for any platform... is to use Http Live Streaming and then you can either use a player that supports HLS or you can simply use the HTML5 video tag. See my updated answer below...
Play a video without a file on disk [Java]

Cannot upload large (>50MB) files to SharePoint 2010 document library

I'm trying to upload a large file to a document library, but it fails after just a few seconds. The upload single document fails silently, upload multiple just shows a failed message. I've turned up the file size limit on the web application to 500MB, and the IIS request length to the same (from this blog), and increased the IIS timeout for good measure. Are there any other size caps that I've missed?
Update I've tried a few files of various sizes, anything 50MB or over fails, so I assume something somewhere is still set to the webapp default.
Update 2 Just tried uploading using the following powershell:
$web = Get-SPWeb http://{site address}
$folder = $web.GetFolder("Site Documents")
$file = Get-Item "C:\mydoc.txt" // ~ 150MB
$folder.Files.Add("SiteDocuments/mydoc.txt", $file.OpenRead(), $false)
and get this exception:
Exception calling "Add" with "3" argument(s): "<nativehr>0x80070003</nativehr><nativestack></nativestack>There is no file with URL 'http://{site address}/SiteDocuments/mydoc.txt' in this Web."
which strikes me as odd as of course the file wouldn't exist until it's been uploaded? N.B. while the document library has the name Site Documents, it has the URL SiteDocuments. Not sure why...
Are you sure you updated the right webapp? Is the filetype blocked by the server? Is there adequate space in your content database? I would check ULS logs after that and see if there is another error since it seems you hit the 3 spots you would need too update.
for uploading a large file, you can use the PUT method instead of using the other ways to upload a document.
by using a put method you will save the file into content database directly. see the example below
Note: the disadvantage of the code below is you cannot catch the object that is responsible for uploading directly, on other word, you cannot update the additional custom properties of the uploaded document directly.
public static bool UploadFileToDocumentLibrary(string sourceFilePath, string targetDocumentLibraryPath)
{
//Flag to indicate whether file was uploaded successfuly or not
bool isUploaded = true;
try
{
// Create a PUT Web request to upload the file.
WebRequest request = WebRequest.Create(targetDocumentLibraryPath);
//Set credentials of the current security context
request.Credentials = CredentialCache.DefaultCredentials;
request.Method = “PUT”;
// Create buffer to transfer file
byte[] fileBuffer = new byte[1024];
// Write the contents of the local file to the request stream.
using (Stream stream = request.GetRequestStream())
{
//Load the content from local file to stream
using (FileStream fsWorkbook = File.Open(sourceFilePath, FileMode.Open, FileAccess.Read))
{
//Get the start point
int startBuffer = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length);
for (int i = startBuffer; i > 0; i = fsWorkbook.Read(fileBuffer, 0, fileBuffer.Length))
{
stream.Write(fileBuffer, 0, i);
}
}
}
// Perform the PUT request
WebResponse response = request.GetResponse();
//Close response
response.Close();
}
catch (Exception ex)
{
//Set the flag to indiacte failure in uploading
isUploaded = false;
}
//Return the final upload status
return isUploaded;
}
and here are an example of calling this method
UploadFileToDocumentLibrary(#”C:\test.txt”, #”http://home-vs/Shared Documents/textfile.pdf”);

Resources