/// <summary> /// Requests a file from a specific url and saves it to the given filePath /// </summary> /// <param name="url"></param> /// <param name="filePath"></param> /// <param name="checksum"></param> /// <param name="ignoreCaching"></param> /// <returns></returns> private async Task <StorageFile> requestAsync(String url, string filePath, String checksum, Boolean ignoreCaching) { StorageFile returnFile = null; using (await downloadLocks.ObtainLock(url).LockAsync().ConfigureAwait(false)) { // fetch file from local storage or download it? bool fileExists = FileUtils.exists(filePath); bool upToDate = await IsFileUpToDateAsync(url, checksum).ConfigureAwait(false); if (fileExists && upToDate) { // retrieve current version from cache returnFile = await FileUtils.ReadFromFile(filePath).ConfigureAwait(false); } else if (NetworkUtils.isOnline()) { // download media file MemoryStream saveStream = await _dataClient.performRequestAsync(new Uri( url )).ConfigureAwait(false); // save data to file returnFile = await FileUtils.WriteToFile(saveStream, filePath).ConfigureAwait(false); await FileCacheIndex.saveAsync(url, saveStream, _config, checksum).ConfigureAwait(false); } else if (FileUtils.exists(filePath)) { // TODO notify app that data might be outdated // no network: use old version from cache (even if no cache index entry exists) returnFile = await FileUtils.ReadFromFile(filePath); } } downloadLocks.ReleaseLock(url); if (returnFile == null) { throw new Exception("Media file " + url + " is not in cache and no internet connection is available."); } return(returnFile); }
private async Task <bool> IsFileUpToDateAsync(string url, string checksum) { FileCacheIndex fileCacheIndex = await FileCacheIndex.retrieveAsync(url, _config).ConfigureAwait(false); if (fileCacheIndex == null) { return(false); } if (checksum != null) { // check with file's checksum return(!fileCacheIndex.IsOutdated(checksum)); } else { // check with collection's last_modified (previewPage.last_changed would be slightly more precise) CollectionCacheIndex collectionCacheIndex = await CollectionCacheIndex.retrieve(_config).ConfigureAwait(false); DateTime collectionLastModified = collectionCacheIndex == null ? default(DateTime) : collectionCacheIndex.lastModified; return(collectionLastModified != null && fileCacheIndex.lastUpdated != null && !(collectionLastModified.CompareTo(fileCacheIndex.lastUpdated) > 0)); } }
/// <summary> /// Used to load and extract an archive to the caches /// </summary> /// <param name="ionFiles"></param> /// <param name="ionPages"></param> /// <param name="url"></param> /// <param name="callback"></param> /// <returns></returns> public async Task loadArchiveAsync(IIonFiles ionFiles, IIonPages ionPages, string url, Action callback = null) { // Temporary used elements in the isolated storage StorageFile archiveFile = null; StorageFolder tempFolder = null; // Get temp-folder path string tempFolderPath = FilePaths.getTempFolderPath(_config); // Lock all used elements using (await _fileLocks.ObtainLock(url).LockAsync().ConfigureAwait(false)) using (await _fileLocks.ObtainLock(tempFolderPath).LockAsync().ConfigureAwait(false)) { try { // Request archive file archiveFile = await ionFiles.requestArchiveFileAsync(url); // Get tempFolder for extraction tempFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(tempFolderPath, CreationCollisionOption.ReplaceExisting); // Generate fileStream from archiveFile using (Stream stream = (Stream)await archiveFile.OpenStreamForReadAsync()) { // Extract file to tempFolder await TarUtils.ExtractTarArchiveAsync(stream, tempFolder).ConfigureAwait(false); } // Get all elements listed in the index file string indexFileString = await FileIO.ReadTextAsync(await tempFolder.GetFileAsync("index.json")); List <ArchiveElement> elementsList = JsonConvert.DeserializeObject <List <ArchiveElement> >(indexFileString); // Handle each element of the index.json for (int i = 0; i < elementsList.Count; i++) { IonRequestInfo requestInfo = PagesURLs.analyze(elementsList[i].url, _config); // Treat every element regarding its type switch (requestInfo.requestType) { case IonRequestType.MEDIA: { // Get all the needed folder- and file names StorageFolder mediaFolder = await ApplicationData.Current.LocalFolder.CreateFolderAsync(FilePaths.getMediaFolderPath(_config, false), CreationCollisionOption.OpenIfExists); string sourcePath = tempFolder.Path + IonConstants.BackSlash + elementsList[i].name.Replace('/', (char)IonConstants.BackSlash[0]); string destinationPath = mediaFolder.Path + IonConstants.BackSlash + FilePaths.getFileName(elementsList[i].url); // Delete a possible existing file if (File.Exists(destinationPath)) { File.Delete(destinationPath); } // Move the file from the temp to the media folder File.Move(sourcePath, destinationPath); // Create index for file FileCacheIndex index = new FileCacheIndex(elementsList[i].url, elementsList[i].checksum, DateTimeUtils.now()); // Save file index await CacheIndexStore.save(elementsList[i].url, index, _config).ConfigureAwait(false); break; } case IonRequestType.PAGE: { // Extract the page json from the file string pageString = await FileIO.ReadTextAsync(await tempFolder.GetFileAsync(elementsList[i].name.Replace('/', (char)IonConstants.BackSlash[0]))); // Parse the new page IonPage page = DataParser.parsePage(pageString); // Save the page to the caches await ionPages.savePageToCachesAsync(page, _config).ConfigureAwait(false); break; } default: { IonLogging.log("Object " + elementsList[i].url + " could not be parsed from archive.", IonLogMessageTypes.ERROR); break; } } } } catch (Exception e) { IonLogging.log("Error at the archive download: " + e.Message, IonLogMessageTypes.ERROR); } finally { // Clean up all temperary used elements if (archiveFile != null) { // Delete index file string indexFilePath = FilePaths.getCacheIndicesFolderPath(_config) + IonConstants.BackSlash + archiveFile.Name + IonConstants.JsonFileExtension; if (File.Exists(indexFilePath)) { File.Delete(indexFilePath); } // Delete archive file await archiveFile.DeleteAsync(); } if (tempFolder != null) { await tempFolder.DeleteAsync(); } // Call the callback action if set before if (callback != null) { callback(); } } } // Release the file locks _fileLocks.ReleaseLock(url); _fileLocks.ReleaseLock(tempFolderPath); }