/// <summary> /// Generates the URLs for a given bundle /// </summary> /// <param name="bundleName"></param> /// <param name="fileExt"></param> /// <param name="debug"></param> /// <returns></returns> private IEnumerable <string> GenerateBundleUrlsAsync(string bundleName, string fileExt, bool debug) { //TODO: We should cache this, but problem is how do we do that with file watchers enabled? We'd still have to lookup the bundleOptions // or maybe we just cache when file watchers are not enabled - probably the way to do it var bundle = _bundleManager.GetBundle(bundleName); if (bundle == null) { throw new BundleNotFoundException(bundleName); } if (bundle.Files.Count == 0) { return(Enumerable.Empty <string>()); } var result = new List <string>(); //get the bundle options from the bundle if they have been set otherwise with the defaults var bundleOptions = bundle.GetBundleOptions(_bundleManager, debug); //if not processing as composite files, then just use their native file paths if (!bundleOptions.ProcessAsCompositeFile) { var files = _fileSetGenerator.GetOrderedFileSet(bundle, _processorFactory.CreateDefault( //the file type in the bundle will always be the same bundle.Files[0].DependencyType)); result.AddRange(files.Select(d => d.FilePath)); return(result); } var cacheBuster = _cacheBusterResolver.GetCacheBuster(bundleOptions.GetCacheBusterType()); var url = _urlManager.GetUrl(bundleName, fileExt, debug, cacheBuster); if (!string.IsNullOrWhiteSpace(url)) { result.Add(url); } return(result); }
/// <summary> /// Handles requests for named bundles /// </summary> /// <param name="bundle">The bundle model</param> /// <returns></returns> public async Task <IActionResult> Bundle( [FromServices] BundleRequestModel bundle) { if (!_bundleManager.TryGetValue(bundle.FileKey, out Bundle foundBundle)) { return(NotFound()); } var bundleOptions = foundBundle.GetBundleOptions(_bundleManager, bundle.Debug); //now we need to determine if this bundle has already been created var compositeFilePath = new FileInfo(_fileSystemHelper.GetCurrentCompositeFilePath(bundle.CacheBuster, bundle.Compression, bundle.FileKey)); if (compositeFilePath.Exists) { _logger.LogDebug($"Returning bundle '{bundle.FileKey}' from cache"); //this is already processed, return it return(File(compositeFilePath.OpenRead(), bundle.Mime)); } //the bundle doesn't exist so we'll go get the files, process them and create the bundle //TODO: We should probably lock here right?! we don't want multiple threads trying to do this at the same time, we'll need a dictionary of locks to do this effectively //get the files for the bundle var files = _fileSetGenerator.GetOrderedFileSet(foundBundle, _processorFactory.CreateDefault( //the file type in the bundle will always be the same foundBundle.Files[0].DependencyType)) .ToArray(); if (files.Length == 0) { return(NotFound()); } using (var bundleContext = new BundleContext(bundle, compositeFilePath)) { var watch = new Stopwatch(); watch.Start(); _logger.LogDebug($"Processing bundle '{bundle.FileKey}', debug? {bundle.Debug} ..."); //we need to do the minify on the original files foreach (var file in files) { await _preProcessManager.ProcessAndCacheFileAsync(file, bundleOptions, bundleContext); } //Get each file path to it's hashed location since that is what the pre-processed file will be saved as Lazy <IFileInfo> fi; var filePaths = files.Select( x => _fileSystemHelper.GetCacheFilePath(x, bundleOptions.FileWatchOptions.Enabled, Path.GetExtension(x.FilePath), bundle.CacheBuster, out fi)); using (var resultStream = await GetCombinedStreamAsync(filePaths, bundleContext)) { //compress the response (if enabled) var compressedStream = await Compressor.CompressAsync( //do not compress anything if it's not enabled in the bundle options bundleOptions.CompressResult?bundle.Compression : CompressionType.none, resultStream); //save the resulting compressed file, if compression is not enabled it will just save the non compressed format // this persisted file will be used in the CheckNotModifiedAttribute which will short circuit the request and return // the raw file if it exists for further requests to this path await CacheCompositeFileAsync(compositeFilePath, compressedStream); _logger.LogDebug($"Processed bundle '{bundle.FileKey}' in {watch.ElapsedMilliseconds}ms"); //return the stream return(File(compressedStream, bundle.Mime)); } } }