I'm trying to download a file and pause it then resume it and pass the latest Range header to it and seek to part i have downloaded in the file then continue writing on it.
The problem is that when i use a cancellation token to pause my download then i lose the part i downloaded in the response object and it becomes null ....so how can i download and write file at the same time in synchronized manner ?
Here is my code:
Windows Store app 8.1 Xaml pausing/resuming download using http classes
and i'm limited in using it because it provides authentication and header in the download.
Please do not suggest using the background downloader api except that you have tested the pause functionality in it your self, because i did test it my self and it rarely worked properly .
The example at the link you provided copies the source stream to the dest stream in a single call. If you want to be able to resume a cancelled download, then you should get the source stream and then read/write small chunks in a loop. Then if the download was cancelled for any reason, before you restart the download you can check the size of the partial file that you have already downloaded and set the appropriate range header.
Edit (Adding sample code):
using (Stream destStream = await destFile.OpenStreamForWriteAsync())
{
using (HttpClient httpClient = new HttpClient())
{
var s = await httpClient.GetInputStreamAsync(new Uri(srcUrl));
using (Stream srcStream = s.AsStreamForRead())
{
byte[] buffer = new byte[32768];
int read;
while ((read = srcStream.Read(buffer, 0, buffer.Length)) > 0)
{
destStream.Write(buffer, 0, read);
}
}
}
}
Related
I'm looking for a way to download large pdf files from an external server with a Flutter application for offline storage.
But downloading a large file (sometimes 100mb+) takes some time. I don't want the app being stuck in a wait function for it to download. What i'm looking for is a download function that has a callback with a progress report (Something like: 250000/500000 bytes done. Doesn't have to be exactly that. Just something that I can work with and make a progress bar out of).
Is this even possible to do in Flutter? The only things I came across were the HTTP library. But that does not seem to have a progress callback and just plainly reading the contents of a http call (Which also doesn't have a progress report). I hope someone has a method for me that I can use to make this happen.
Kind regards,
Kevin Walter
EDIT:
C# has the perfect example of what I mean
https://stackoverflow.com/a/9459441/2854656
https://docs.flutter.io/flutter/dart-io/HttpClient-class.html
https://docs.flutter.io/flutter/dart-io/HttpClientResponse-class.html
int fileSize;
int downloadProgress = 0;
new HttpClient().get('localhost', 80, '/file.txt')
.then((HttpClientRequest request) => request.close())
.then((HttpClientResponse response) {
fileSize ??= respone.contentLength;
response.transform(utf8.decoder).listen((contents) {
downloadProgres += contents.length;
// handle data
});
});
So currently working on a project and experiencing a strange issue with the ios version of Safari involving the playback of an audio file from a server.
I'm currently facing the following issue:
Person comes onto the page which has a standard html5 audio tag, and a direct link to the audio file for downloading purposes.
Person tries to listen to audio from audio tag, content plays for x number of minutes, cuts off then repeats (the x number of minutes is NOT the length of the recording, and is not consistent).
Person tries direct link of recording, rather then downloading the recording, Safari appears to go to a new page and wraps the download url in a video element, and the same issue as step 2 occurs.
Now the audio file is served up via a java scriptlet, which serves the file with the following code snippet:
String fn = saveTo + file_name;
f = new File(fn);
String fname = f.getName();
String contentType = "audio/wav";
if(fname.endsWith("mp3")){
contentType = "audio/mp3";
}
response.setContentType(contentType);
response.setHeader("Content-Transfer-Encoding", "binary");
response.setHeader("Content-disposition", "attachment;filename="+f.getName());
response.setHeader("Content-Length", ""+f.length());
FileInputStream fin = null;
try{
fin = new FileInputStream(f.getCanonicalFile());
byte[] data = new byte[1024];
int x = 0;
while((x = fin.read(data, 0, 1024))>=0){
response.getOutputStream().write(data, 0, x);
Thread.sleep(1);
}
} finally {
if(fin != null) {
try{
fin.close();
}catch(Exception ex){}
}
}
Now I know the code isn't the best by any measure, it isn't my code, and we're obviously working on the assumption that the file is found.
I'm finding when debugging on the iPhone with debug mode on a mac, it doesn't seem to show a return status code. It shows no response headers but it obviously must be receiving something. The server log seems to think its returning a status 200, this showing in Chrome and Firefox.
The code above appears to work fine with Chrome, and Firefox, but not Safari.
The only thing I am guessing is it has something to do with how the file is being pushed to the output stream that Safari isn't liking, or maybe its getting confused and should have a different status code. I've been banging my head against this for a good few days, and reading as much as I can about Safari, though most of the documentation I'm finding is on its "unique" implementation of web audio, and the use of a single channel which seems to be irrelevant to this.
Any Help would be appreciated.
I experienced the same issue with Safari on iOS, and after a lot of debugging, I found the issue was related to the combination of headers applied to the response.
My application is C#-based, but this solution should be platform independent (because as previously stated, it is a response header issue).
Necessary Headers:
Content-Range: bytes 0-[content length]/[content length]
Content-Transfer-Encoding: binary
Content-Length: [content length]
Accept-Ranges: bytes
I devised this after inspecting a response from MP3s delivered via Akamai's content delivery service.
When I use the Download method of the FileReference class, everything works fine on Desktop and Android, but I get an error on iOS.
This is the code:
var req = new URLRequest(url);
var localRef:FileReference = new FileReference();
localRef.download(req);
On iOS I'm getting an Alert:
Download Error
File downloads not supported.
I already tried to NavigateToUrl() and it asks save the file in Dropbox or another App.
How can I fix this error?
You shouldn't use FileReference on mobile (or AIR, in general, though it does open the Load/Save dialog on desktop so there can be some use there). You should instead use File and FileStream, which give you far more control over the file system.
In this case, you could try to use File.download() and save it to File.applicationStorageDirectory, but I don't know if it will have any difference since it extends FileReference.
What I generally do is use URLStream instead of URLLoader. It gives you access to the raw bytes of the file you are downloading and then use File and FileStream
So something like (and this is untested off the top of my head, though I have used similar in the past):
var urlStream:URLStream = new URLStream();
urlStream.addEventListener(Event.COMPLETE, completeHandler);
urlStream.load(new URLLoader('url');
function completeHandler(e:Event):void {
var bytes:ByteArray = new ByteArray();
urlStream.readBytes(bytes);
var f:File = File.applicationStorageDirectory.resolvePath('filename');
var fs:FileStream = new FileStream();
fs.open(f, FileMode.WRITE);
fs.writeBytes(bytes);
fs.close();
}
Now, obviously, there is a lot more you want to account for (errors, progress, etc). That should be enough to point you in the right direction, however.
It's possible to create a full download manager using this method (something I did for an iOS project two years ago), since you can save as-you-go to the file system rather than waiting until Event.COMPLETE fires (using the ProgressEvent.PROGRESS event). That allows you to avoid having a 500MB file in memory, something most devices can't handle.
I am trying to upload so many files via Azure Blob Storage .NET api and using with the current latest version 4.0.1. In ASP.NET MVC application i use async action method to upload via await blobFile.UploadFromStreamAsync but it really doesn't work and even i don't see an exception. It silently stops in that method without success.
But if i change action method to none-async and upload via blobFile.UploadFromStream method then everything to works well. I may uploaded via async way with 1% success rate that means very very low stability.
Do you experience same thing ? Is it bug in Storage Api implementation ?
Here is short example. One is async and the other one is none async action methods. There is no any problem if i upload small files but problem appears on large downloads. In this example UploadBlobSec method upload in short time but UploadBlob takes endless time.
public async Task UploadBlob()
{
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["AzureStorage"].ConnectionString);
var blobContainer = storageAccount.CreateCloudBlobClient().GetContainerReference("files");
var blobFile = blobContainer.GetBlockBlobReference("song.mp3");
using (var stream = new WebClient().OpenRead("http://apolyonstorage.blob.core.windows.net/files/e8b1a1fa-8791-44dc-92ce-1a67a62f7b0f.mp3"))
{
await blobFile.UploadFromStreamAsync(stream);
}
}
public void UploadBlobSec()
{
var storageAccount = CloudStorageAccount.Parse(ConfigurationManager.ConnectionStrings["AzureStorage"].ConnectionString);
var blobContainer = storageAccount.CreateCloudBlobClient().GetContainerReference("files");
var blobFile = blobContainer.GetBlockBlobReference("song.mp3");
using (var stream = new WebClient().OpenRead("http://apolyonstorage.blob.core.windows.net/files/e8b1a1fa-8791-44dc-92ce-1a67a62f7b0f.mp3"))
{
blobFile.UploadFromStream(stream);
}
}
Code snippet looks fine - although I am not sure what the application around it is doing. Have you turned on server logs to see what is happening server side? For large files you should see the async upload translated into a couple of PutBlocks and then a PutBlockList. If you don't see the PutBlockList them maybe something strange is happening in your application.
Then assuming you do see the operations in the server logs that obviously means the operations are occurring. At that point look at the E2ELatency numbers vs ServerLatency I think you will see a large difference with E2Latency being much higher as it incorporates the time spent client side - and it would be interesting to see if your client network could be contributing to the problem. For example on my connection the e2elatency on the first PutBlock was 1346 ms vs 137 for ServerLatency.
For more information on logging take a look here.
Jason
I am aiming to create an application where the user can take a picture of their face, which includes an overlay of a face cutout. I need the user to be able to click the screen and for the application to save the picture, mask it with the same face cutout, and then save it to the applications storage.
This is the first time using AIR on IOS with Actionscript3. I know there is a proper directory that you are supposed to save to on IOS however I am not aware of it. I have been saving other variables using SharedObjects...
E.g:
var so:SharedObject = SharedObject.getLocal("applicationID");
and then writing to it
so.data['variableID'] = aVariable;
This is how I access the front camera and display it. For some reason to display the whole video and not a narrow section of it, I add the video from the camera to a movieclip on the stage, 50% of the size of the stage.
import flash.media.Camera;
import flash.media.Video;
import flash.display.BitmapData;
import flash.utils.ByteArray;
import com.adobe.images.JPGEncoder
var camera:Camera = Camera.getCamera("1");
camera.setQuality(0,100);
camera.setMode(1024,768, 30, false);
var video:Video = new Video();
video.attachCamera(camera);
videoArea.addChild(video);
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Capture_Picture_BTN.addEventListener(TouchEvent.TOUCH_TAP, savePicture);
function savePicture(event:TouchEvent):void
{
trace("Saving Picture");
//Capture Picture BTN
var bitmapData:BitmapData = new BitmapData(1024,768);
bitmapData.draw(video);
}
I apologize if this is the wrong way of going about this I am still fairly new to Actionscript as it is. If you need any more information I will be happy to provide.
You can only save ~100kb of data via SharedObject, so you can't use that. It is meant solely to save application settings and, from my experience, is ignored by AIR devs because we have better control over the file system.
We have the File and FileStream classes. These classes allow you to read and write directly to and from the device's disk, something not quite possible on the web (the user is the one who has to save/open; can't be done automatically).
Before my example, I must stress that you should read the documentation. Adobe's LiveDocs are among the best language/SDK docs available and it will point out many things that my quick example on usage will not (such as in-depth discussion of each directory, how to write various types, etc)
So, here's an example:
// create the File and resolve it to the applicationStorageDirectory, which is where you should save files
var f:File = File.applicationStorageDirectory.resolvePath("name.png");
// this prevents iCloud backup. false by default. Apple will reject anything using this directory for large file saving that doesn't prevent iCloud backup. Can also use cacheDirectory, though certain aspects of AIR cannot access that directory
f.preventBackup = true;
// set up the filestream
var fs:FileStream = new FileStream();
fs.open(f, FileMode.WRITE); //open file to write
fs.writeBytes( BYTE ARRAY HERE ); // writes a byte array to file
fs.close(); // close connection
So that will save to disk. To read, you open the FileStream in READ mode.
var fs:FileStream = new FileStream();
var output:ByteArray = new ByteArray();
fs.open(f, FileMode.READ); //open file to write
fs.readBytes( output ); // reads the file into a byte array
fs.close(); // close connection
Again, please read the documentation. FileStream supports dozens of read and write methods of various types. You need to select the correct one for your situation (readBytes() and writeBytes() should work in all cases, though there are instances where you are should use a more specific method)
Hope that helps.