/// <summary> /// Provides an end method for an asynchronous process. /// </summary> /// <param name="result">An IAsyncResult that contains information about the status of the process.</param> public void EndProcessRequest( IAsyncResult result ) { // restore the context from the asyncResult.AsyncState HttpContext context = (HttpContext)result.AsyncState; try { context.Response.Clear(); bool isBinaryFile = (bool)context.Items["isBinaryFile"]; if ( isBinaryFile ) { var rockContext = new RockContext(); bool requiresViewSecurity = false; BinaryFile binaryFile = new BinaryFileService( rockContext ).EndGet( result, context, out requiresViewSecurity ); if ( binaryFile != null ) { //// if the binaryFile's BinaryFileType requires security, check security //// note: we put a RequiresViewSecurity flag on BinaryFileType because checking security for every file would be slow (~40ms+ per request) if ( requiresViewSecurity ) { var currentUser = new UserLoginService( rockContext ).GetByUserName( UserLogin.GetCurrentUserName() ); Person currentPerson = currentUser != null ? currentUser.Person : null; binaryFile.BinaryFileType = binaryFile.BinaryFileType ?? new BinaryFileTypeService( rockContext ).Get( binaryFile.BinaryFileTypeId.Value ); if ( !binaryFile.IsAuthorized( Authorization.VIEW, currentPerson ) ) { SendNotAuthorized( context ); return; } } SendFile( context, binaryFile.ContentStream, binaryFile.MimeType, binaryFile.FileName, binaryFile.Guid.ToString("N") ); return; } } else { Stream fileContents = (Stream)context.Items["fileContents"]; string physicalContentFileName = context.Items["physicalContentFileName"] as string; if ( fileContents != null ) { string mimeType = System.Web.MimeMapping.GetMimeMapping( physicalContentFileName ); string fileName = Path.GetFileName( physicalContentFileName ); SendFile( context, fileContents, mimeType, fileName, "" ); return; } } context.Response.StatusCode = 404; context.Response.StatusDescription = "Unable to find the requested file."; } catch ( Exception ex ) { ExceptionLogService.LogException( ex, context ); try { context.Response.StatusCode = 500; context.Response.StatusDescription = ex.Message; context.Response.Flush(); context.ApplicationInstance.CompleteRequest(); } catch ( Exception ex2 ) { ExceptionLogService.LogException( ex2, context ); } } }
/// <summary> /// Processes the binary file request. /// </summary> /// <param name="context">The context.</param> private void ProcessBinaryFileRequest( HttpContext context ) { int fileId = context.Request.QueryString["id"].AsInteger() ?? 0; Guid fileGuid = context.Request.QueryString["guid"].AsGuid(); if ( fileId == 0 && fileGuid.Equals( Guid.Empty ) ) { SendNotFound( context ); } var rockContext = new RockContext(); var binaryFileQuery = new BinaryFileService( rockContext ).Queryable(); if ( fileGuid != Guid.Empty ) { binaryFileQuery = binaryFileQuery.Where( a => a.Guid == fileGuid ); } else { binaryFileQuery = binaryFileQuery.Where( a => a.Id == fileId ); } //// get just the binaryFileMetaData (not the file content) just in case we can get the filecontent faster from the cache //// a null ModifiedDateTime shouldn't happen, but just in case, set it to DateTime.MaxValue so we error on the side of not getting it from the cache var binaryFileMetaData = binaryFileQuery.Select( a => new { BinaryFileType_AllowCaching = a.BinaryFileType.AllowCaching, BinaryFileType_RequiresSecurity = a.BinaryFileType.RequiresSecurity, ModifiedDateTime = a.ModifiedDateTime ?? DateTime.MaxValue, a.MimeType, a.FileName } ).FirstOrDefault(); if ( binaryFileMetaData == null ) { SendNotFound( context ); return; } //// if the binaryFile's BinaryFileType requires security, check security //// note: we put a RequiresSecurity flag on BinaryFileType because checking security for every image would be slow (~40ms+ per image request) if ( binaryFileMetaData.BinaryFileType_RequiresSecurity ) { var currentUser = new UserLoginService( rockContext ).GetByUserName( UserLogin.GetCurrentUserName() ); Person currentPerson = currentUser != null ? currentUser.Person : null; BinaryFile binaryFileAuth = new BinaryFileService( rockContext ).Queryable( "BinaryFileType" ).First( a => a.Guid == fileGuid || a.Id == fileId ); if ( !binaryFileAuth.IsAuthorized( Authorization.VIEW, currentPerson ) ) { SendNotAuthorized( context ); return; } } byte[] fileContent = null; // Is it cached string cacheName = UrlQueryToCachedFileName( context.Request.QueryString, binaryFileMetaData.MimeType ); string physCachedFilePath = context.Request.MapPath( string.Format( "~/App_Data/Cache/{0}", cacheName ) ); if ( binaryFileMetaData.BinaryFileType_AllowCaching && File.Exists( physCachedFilePath ) ) { //// Compare the File's Creation DateTime (which comes from the OS's clock), adjust it for the Rock OrgTimeZone, then compare to BinaryFile's ModifiedDateTime (which is already in OrgTimeZone). //// If the BinaryFile record in the database is less recent than the last time this was cached, it is safe to use the Cached version. //// NOTE: A BinaryFile record is typically just added and never modified (a modify is just creating a new BinaryFile record and deleting the old one), so the cached version will probably always be the correct choice. DateTime cachedFileDateTime = RockDateTime.ConvertLocalDateTimeToRockDateTime( File.GetCreationTime( physCachedFilePath ) ); if ( binaryFileMetaData.ModifiedDateTime < cachedFileDateTime ) { // NOTE: the cached file has already been resized (the size is part of the cached file's filename), so we don't need to resize it again fileContent = FetchFromCache( physCachedFilePath ); } } if ( fileContent == null ) { // If we didn't get it from the cache, get it from the binaryFileService BinaryFile binaryFile = GetFromBinaryFileService( context, fileId, fileGuid ); if ( binaryFile != null && binaryFile.Data != null ) { fileContent = binaryFile.Data.Content; } // If we got the image from the binaryFileService, it might need to be resized and cached if ( fileContent != null ) { // If more than 1 query string param is passed in, assume resize is needed if ( context.Request.QueryString.Count > 1 ) { // if it isn't an SVG file, do a Resize if ( binaryFile.MimeType != "image/svg+xml" ) { fileContent = GetResized( context.Request.QueryString, fileContent ); } } if ( binaryFileMetaData.BinaryFileType_AllowCaching ) { Cache( fileContent, physCachedFilePath ); } } } if ( fileContent == null ) { // if we couldn't get the file from the binaryFileServie or the cache, respond with NotFound SendNotFound( context ); return; } // respond with File context.Response.ContentType = binaryFileMetaData.MimeType; context.Response.AddHeader( "content-disposition", "inline;filename=" + binaryFileMetaData.FileName ); if ( binaryFileMetaData.BinaryFileType_AllowCaching ) { // if binaryFileType is set to allowcaching, also tell the browser to cache it for 365 days context.Response.Cache.SetLastModified( binaryFileMetaData.ModifiedDateTime ); context.Response.Cache.SetMaxAge( new TimeSpan( 365, 0, 0, 0 ) ); } context.Response.BinaryWrite( fileContent ); context.Response.Flush(); }