/// <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; if (context != null) { context.Response.Clear(); 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) { binaryFile.BinaryFileType = binaryFile.BinaryFileType ?? new BinaryFileTypeService(rockContext).Get(binaryFile.BinaryFileTypeId.Value); var currentUser = new UserLoginService(rockContext).GetByUserName(UserLogin.GetCurrentUserName()); Person currentPerson = currentUser != null ? currentUser.Person : null; if (!binaryFile.IsAuthorized(Authorization.VIEW, currentPerson)) { SendError(context, 403, "Not authorized to view file."); return; } } var binaryFileType = BinaryFileTypeCache.Get(binaryFile.BinaryFileTypeId.Value); SendFile(context, binaryFile.ContentStream, binaryFile.MimeType, binaryFile.FileName, binaryFile.Guid.ToString("N"), binaryFileType.CacheControlHeader); return; } SendError(context, 404, "File could not be found."); } }
/// <summary> /// Processes the binary file request. /// </summary> /// <param name="context">The context.</param> private void ProcessBinaryFileRequest(HttpContext context, RockContext rockContext) { int fileId = context.Request.QueryString["id"].AsInteger(); Guid fileGuid = context.Request.QueryString["guid"].AsGuid(); if (fileId == 0 && fileGuid == Guid.Empty) { SendBadRequest(context, "File id key must be a guid or an int."); return; } 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 { a.Id, BinaryFileType_CacheToServerFileSystem = a.BinaryFileType.CacheToServerFileSystem, BinaryFileType_RequiresViewSecurity = a.BinaryFileType.RequiresViewSecurity, a.BinaryFileTypeId, ModifiedDateTime = a.ModifiedDateTime ?? DateTime.MaxValue, a.MimeType, a.FileName }).FirstOrDefault(); if (binaryFileMetaData == null) { SendNotFound(context); return; } //// if the binaryFile's BinaryFileType requires view security, check security //// note: we put a RequiresViewSecurity flag on BinaryFileType because checking security for every image would be slow (~40ms+ per image request) if (binaryFileMetaData.BinaryFileType_RequiresViewSecurity) { var currentUser = new UserLoginService(rockContext).GetByUserName(UserLogin.GetCurrentUserName()); Person currentPerson = currentUser != null ? currentUser.Person : null; BinaryFile binaryFileAuth = new BinaryFileService(rockContext).Queryable("BinaryFileType").AsNoTracking().First(a => a.Id == binaryFileMetaData.Id); if (!binaryFileAuth.IsAuthorized(Authorization.VIEW, currentPerson)) { SendNotAuthorized(context); return; } } Stream fileContent = null; try { // 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_CacheToServerFileSystem && File.Exists(physCachedFilePath)) { //// Compare the File's LastWrite 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.GetLastWriteTime(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, binaryFileMetaData.Id, rockContext); if (binaryFile != null) { fileContent = binaryFile.ContentStream; } // 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, or the mime type is TIFF, assume resize is needed // Note: we force "image/tiff" to get resized so that it gets converted into a jpg (browsers don't like tiffs) if (context.Request.QueryString.Count > GetQueryCount(context) || binaryFile.MimeType == "image/tiff") { // 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_CacheToServerFileSystem) { Cache(fileContent, physCachedFilePath); // Reset stream if (fileContent.CanSeek) { fileContent.Seek(0, SeekOrigin.Begin); } else { fileContent = FetchFromCache(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 if (binaryFileMetaData.BinaryFileTypeId != null) { var binaryFileCache = BinaryFileTypeCache.Get(binaryFileMetaData.BinaryFileTypeId.Value); binaryFileCache.CacheControlHeader.SetupHttpCachePolicy(context.Response.Cache); } // set the mime-type to that of the binary file context.Response.ContentType = binaryFileMetaData.MimeType != "image/tiff" ? binaryFileMetaData.MimeType : "image/jpg"; // check that the format of the image wasn't changed by a format query parm if so adjust the mime-type to reflect the conversion if (context.Request["format"].IsNotNullOrWhiteSpace()) { switch (context.Request["format"]) { case "png": { context.Response.ContentType = "image/png"; break; } case "gif": { context.Response.ContentType = "image/gif"; break; } case "jpg": { context.Response.ContentType = "image/jpeg"; break; } } } using (var responseStream = fileContent) { context.Response.AddHeader("content-disposition", "inline;filename=" + binaryFileMetaData.FileName.MakeValidFileName().UrlEncode()); if (responseStream.CanSeek) { responseStream.Seek(0, SeekOrigin.Begin); } responseStream.CopyTo(context.Response.OutputStream); context.Response.Flush(); } } finally { if (fileContent != null) { fileContent.Dispose(); } } }
/// <summary> /// Gets the cache object associated with this Entity /// </summary> /// <returns></returns> public IEntityCache GetCacheObject() { return(BinaryFileTypeCache.Get(this.Id)); }