public bool Execute(IBackloadContext context, IDeleteFilesRequestParam param) { // In this extension we delete the copies we've created within the _Backup and _Temp folder. // Backload has made the copies within the _Backup folder automatically, because it is configured to do so (config file) // The copies within the _Temp folder were created for demo purposes in the StoreFileRequest extension. string dirBackup = string.Empty; string dirTemp = string.Empty; if (param.DeleteFiles == null) return false; // in the case something went wrong. if (param.DeleteFiles.Files.Count > 0) { dirTemp = Path.GetDirectoryName(Helper.GetLocalFilePath(context.Request, param.DeleteFiles.First().FileUrl).Replace("\\Uploads", "\\_Temp")); // Helper method in Backload.Contracts to get the full local file path dirBackup = Path.GetDirectoryName(Helper.GetLocalFilePath(context.Request, param.DeleteFiles.First().FileUrl).Replace("\\Uploads", "\\_Backup")); // Helper method in Backload.Contracts to get the full local file path } foreach (var file in param.DeleteFiles.Files) { // Synchronous deletion: // Delete the backup file Backload created (see config file) string pathBackupFile = Path.Combine(dirBackup, file.FileName); if (File.Exists(pathBackupFile)) File.Delete(pathBackupFile); // Asynchronous deletion with Tasks: // Demo 6: Running and awaiting a method asynchronously in a thread of the AppDomains thread pool. // Note: We do not use await here, because we have implemented the synchronous interface. // The Tasks will be awaited at the end of the extension point for the FilesDelete extension. If we do not await the // result, the task may be aborted and does not finish file deletion. var t = Task.Run(() => DeleteTempFiles(dirTemp, file.FileName)); context.PipelineControl.TaskManager.ExtensionPointTasks.Add(t); } return (param.DeleteFiles.Files.Count > 0); }
public bool Execute(IBackloadContext context, IIncomingRequestParam param) { // Demo 2: Comment out the following line and rebuild the solution return(false); if (context.HttpMethod == "POST") { int startIdx = context.PipelineControl.Message.MessageCode; string subfolder = string.Empty; if (startIdx < 8) { subfolder = DateTime.Now.ToString(datetime[startIdx]); } else { subfolder = lorem[startIdx - 8]; } if (string.IsNullOrEmpty(param.BackloadValues.UploadContext) == false) { param.BackloadValues.UploadContext += ";"; // Seperate subfolders with a semicolon } param.BackloadValues.UploadContext += subfolder; // There are multiple ways sharing data between extensions. Here we use PipelineControl.Message to increase the index. // Note, that the ExtensionLogger logs the messages (subfolders) in PipelineControl.Message returned from the extension context.PipelineControl.Message.MessageText = subfolder; context.PipelineControl.Message.MessageCode += 1; return(true); // Processed value is true, because the extension has changed properties. } return(false); // No properties have been changed, so false is returned. }
public bool Execute(IBackloadContext context, IIncomingRequestParam param) { if (context.HttpMethod == "POST") { // This extension validate if the artist is in the database, and returns a Bad Request status (400) if not. // IMPORTANT: Don't forget to rebuild the extension when you changed code. Otherwise you may use the old extensions code. using (var artists = new ArtistsLibrary()) { var artist = artists.Artists.FirstOrDefault(a => a.ArtistId == param.BackloadValues.ObjectContext); if (artist == null) // Artist not in list { // Stop further processing of the pipeline but all extensions can do their work (maybe logging, etc.). // The outgoing extension will also be called, so you have the chance to change the response to the client. context.PipelineControl.ExecutePipeline = false; // Because we prevented the execution of the core pipeline (where the core method for executing // this request is) we do not generate a FileUploadStatus which holds the status of all files this request // handles. IIncomingRequest is the first extension point and FileUploadStatus is generated later in the core pipeline. // This taken into account, we cannot send a message with each FileUploadStatusItem, instead we send a general error. // If you want to send messages with the FileUploadStatus, do this in an extension like GetFilesRequest, StoreFileRequest // or in the OutgoingResponse extension. context.Request.RequestContext.HttpContext.Response.StatusCode = 400; return(true); // Return value is true, because the extension has changed properties. } } } return(false); // No properties have been changed, so false is returned. }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // Note: We use convention based plugin handling (see Example 09). // Backload v1.9 and above can handle this internally. Just send plugin=plupload in the querystring or in the config file. // // In our example we have a PlUpload Plugin client side, so we transform the output to a PlUpload friendly format. // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // // Remarks: Don't forget to rebuild your solution if you made changes to your extension, otherwise you may use the old extension assembly. if (param.FileStatus == null) { return(false); // in the case something went wrong. } var result = new PlUploadFiles(); foreach (var file in param.FileStatus.Files) { if (context.HttpMethod != "DELETE") { result.files.Add(new PlUploadFile(file.Success, file.FileName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); } else { result.files.Add(new PlUploadFileCore(file.Success, file.FileName, file.ErrorMessage)); //We only return values we need on client side, so we instatiate a PlFileCore object } } param.Result = Helper.Json(result); return(true); // We've processed the response }
public bool Execute(IBackloadContext context, IIncomingRequestParam param) { if (context.HttpMethod == "POST") { // This extension validate if the artist is in the database, and returns a Bad Request status (400) if not. // IMPORTANT: Don't forget to rebuild the extension when you changed code. Otherwise you may use the old extensions code. using (var artists = new ArtistsLibrary()) { var artist = artists.Artists.FirstOrDefault(a => a.ArtistId == param.BackloadValues.ObjectContext); if (artist == null) // Artist not in list { // Stop further processing of the pipeline but all extensions can do their work (maybe logging, etc.). // The outgoing extension will also be called, so you have the chance to change the response to the client. context.PipelineControl.ExecutePipeline = false; // Because we prevented the execution of the core pipeline (where the core method for executing // this request is) we do not generate a FileUploadStatus which holds the status of all files this request // handles. IIncomingRequest is the first extension point and FileUploadStatus is generated later in the core pipeline. // This taken into account, we cannot send a message with each FileUploadStatusItem, instead we send a general error. // If you want to send messages with the FileUploadStatus, do this in an extension like GetFilesRequest, StoreFileRequest // or in the OutgoingResponse extension. context.Request.RequestContext.HttpContext.Response.StatusCode = 400; return true; // Return value is true, because the extension has changed properties. } } } return false; // No properties have been changed, so false is returned. }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // Don't forget to rebuid the extension if you made changes to your extension, otherwise you may use the old extension assembly. if (context.RequestType == RequestType.Default) { var result = new PlUploadFiles(); foreach (var file in param.FileStatus.Files) { if (context.HttpMethod != "DELETE") { result.files.Add(new PlUploadFile(file.Success, file.FileName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); } else { result.files.Add(new PlUploadFileCore(file.Success, file.FileName, file.ErrorMessage)); //We only return values we need on client side, so we return a PlFileCode object } } param.Result = Helper.Json(result); return(param.FileStatus.Files.Count > 0); // we have processed the response, so we notify the pipeline } return(false); }
public bool Execute(IBackloadContext context, IIncomingRequestParam param) { // Demo 2: Comment out the following line and rebuild the solution return false; if (context.HttpMethod == "POST") { int startIdx = context.PipelineControl.Message.MessageCode; string subfolder = string.Empty; if (startIdx < 8) subfolder = DateTime.Now.ToString(datetime[startIdx]); else subfolder = lorem[startIdx - 8]; if (string.IsNullOrEmpty(param.BackloadValues.UploadContext) == false) param.BackloadValues.UploadContext += ";"; // Seperate subfolders with a semicolon param.BackloadValues.UploadContext += subfolder; // There are multiple ways sharing data between extensions. Here we use PipelineControl.Message to increase the index. // Note, that the ExtensionLogger logs the messages (subfolders) in PipelineControl.Message returned from the extension context.PipelineControl.Message.MessageText = subfolder; context.PipelineControl.Message.MessageCode += 1; return true; // Processed value is true, because the extension has changed properties. } return false; // No properties have been changed, so false is returned. }
public bool Execute(IBackloadContext context, IProcessPipelineExceptionParam param) { // // Example 1: Graceful exception. We send an error message in our response // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual. // Note: In this case the error is not handled in our client side error handler as we do not // send a HTTP status error code (see examples 2 and 3). Instead the response was send to our // client side FilesAdded handler, so we need an if statement reading the file.success property there. // This could lead to a lot of messy code, because you have to notify the client uploader yourself that something // went wrong. PlUpload isn't very good in changing the state from your code. Example 2 provides a cleaner solution. // // Don't forget to rebuild your extension if you have not set a project dependency in your MVC app context.Request.RequestContext.HttpContext.Response.StatusCode = 200; // Backload has set 500 Internal Server Error, we revert this for demo purposes if ((param.FileStatus != null) && (param.FileStatus.Files.Count > 0)) { foreach (var file in param.FileStatus.Files) { file.ErrorMessage = param.Exception.Message; // for simplicity we set the error to all FileUploadStatusItems file.Success = false; // for simplicity we set the error to all FileUploadStatusItems } } else { // For demo only. We build our own FileStatus. FileStatusSimple has no logic implemented, its only a property bag. FileStatusSimple status = new FileStatusSimple(); for (int i = 0; i < context.Request.Files.Count; i++) { var file = context.Request.Files[i]; status.Files.Add(new FileStatusSimpleItem(file.FileName, file.ContentType, file.ContentLength, "", param.Exception.Message, false)); } // Now that we have a simple IFileUploadStatus object it can be transformed in our outgoing extension // into a PlUpload friendly Json format. param.FileStatus = status; } // // Example 2: Set Response code to a valid http status code (e.g. 500 Internal server error. // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual and can build a custom Json response // which will be sent with the HTTP status code. The client error handler can use the Json object. // Comment in the following line (Don't forget to rebuild your extension if you have not set a project dependency in your MVC app): // // request.RequestContext.HttpContext.Response.StatusCode = 500; // Note since v1.9 Backload sets this status code automatically on exceptions // // Example 3: We throw an exception, so ASP.MVC sends out an http status code. // Extensions responsible for the outgoing response (IOutgoingResponse) are not executed anymore. // The response body is null in this case, because no JsonResult has been created. // Comment in the following line (Don't forget to rebuild your extension if you have not set a project dependency in your MVC app): // // throw new HttpException(500, param.Exception.Message, Exception); return(true); // Set to true, if the extension has manipulated the result. }
public bool Execute(IBackloadContext context, IProcessPipelineExceptionParam param) { // // Example 1: Graceful exception. We send an error message in our response // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual. // Note: In this case the error is not handled in our client side error handler as we do not // send a HTTP status error code (see examples 2 and 3). Instead the response was send to our // client side FilesAdded handler, so we need an if statement reading the file.success property there. // This could lead to a lot of messy code, because you have to notify the client uploader yourself that something // went wrong. PlUpload isn't very good in changing the state from your code. Example 2 provides a cleaner solution. // // Don't forget to rebuild your extension if you have not set a project dependency in your MVC app context.Request.RequestContext.HttpContext.Response.StatusCode = 200; // Backload has set 500 Internal Server Error, we revert this for demo purposes if ((param.FileStatus != null) && (param.FileStatus.Files.Count > 0)) { foreach (var file in param.FileStatus.Files) { file.ErrorMessage = param.Exception.Message; // for simplicity we set the error to all FileUploadStatusItems file.Success = false; // for simplicity we set the error to all FileUploadStatusItems } } else { // For demo only. We build our own FileStatus. FileStatusSimple has no logic implemented, its only a property bag. FileStatusSimple status = new FileStatusSimple(); for (int i = 0; i < context.Request.Files.Count; i++) { var file = context.Request.Files[i]; status.Files.Add(new FileStatusSimpleItem(file.FileName, file.ContentType, file.ContentLength, "", param.Exception.Message, false)); } // Now that we have a simple IFileUploadStatus object it can be transformed in our outgoing extension // into a PlUpload friendly Json format. param.FileStatus = status; } // // Example 2: Set Response code to a valid http status code (e.g. 500 Internal server error. // Extensions responsible for the outgoing response (IOutgoingResponse) are executed as usual and can build a custom Json response // which will be sent with the HTTP status code. The client error handler can use the Json object. // Comment in the following line (Don't forget to rebuild your extension if you have not set a project dependency in your MVC app): // // request.RequestContext.HttpContext.Response.StatusCode = 500; // Note since v1.9 Backload sets this status code automatically on exceptions // // Example 3: We throw an exception, so ASP.MVC sends out an http status code. // Extensions responsible for the outgoing response (IOutgoingResponse) are not executed anymore. // The response body is null in this case, because no JsonResult has been created. // Comment in the following line (Don't forget to rebuild your extension if you have not set a project dependency in your MVC app): // // throw new HttpException(500, param.Exception.Message, Exception); return true; // Set to true, if the extension has manipulated the result. }
public async Task <bool> ExecuteAsync(IBackloadContext context, IGetFilesRequestParam param) { if (param.FileStatus == null) { return(false); // in the case something went wrong. } // In this example we want the user to work with a backup file we created on file upload. // Note: We do not need to use async/await or Tasks in this simple example. We only simulate a long running task. // Important notes: In order to test these demos set breakpoints in all extensions at the beginning of the // ProcessStep/ProcessStepAsync method. After changing the code always rebuild this extension or set a project dependency // for the extension (context menu or Project menu) in your MVC project as we did, so the extension class is automatically // rebuilded. The output build path of the extension is set to the main MVC project in this solution (folder ~/bin/extensions/). // If you do not rebuild the extension, the main MVC application may uses the old extension. // Demo 1: The following line simulates a long running task. We do not care about (await) the results of this task, so it // does not block. The task runs as a background thread within the AppDomains thread pool. You should not use this technique // with file IO, because the thread may be aborted when Backload returns. (If you do not want to await the results of a long // running File I/O operation take a look in the StoreFile extension were we're using a separate thread). var t = Task.Run(() => Task.Delay(System.TimeSpan.FromSeconds(10))); // Demo 2: Uncomment the line below, set breakpoints (also in the GetFilesRequest2 extension) and rebuild the extension. // You'll notice that debugging stops and awaits the result of the task. // The subsequent code and extensions are not executed until the task is finished. But, the ui thread is not blocked and tied up. // You can see this, because the web page comes back. Usually, when ececuting and debug synchronous code, the web page // doesn't come back in long running tasks. // await t; // Demo 3: Comment out the await line above, uncomment the following line, set breakpoints and rebuild the extension. // The debugging now doesn't stop here and the rest of this extensions code (and the extension handling code in the extension manager) // are executed. Before calling the next extension whithin this extension point, the extension manager awaits the result. Note that // the second extension below isn't called until the task is finished. // context.PipelineControl.TaskManager.ExtensionTasks.Add(t); // Demo 4: Comment out the ExtensionTasks.Add line above, uncomment the following line, set breakpoints and rebuild the extension. // When debugging the application you'll see, that the second extension also can execute and finish its code, before the // extension manager awaits at the end of the extension point (before code execution returns to the pipeline) the result. // context.PipelineControl.TaskManager.ExtensionPointTasks.Add(t); // Demo 5: Comment out the ExtensionPointTasks.Add line above, uncomment the following line, set breakpoints and rebuild the extension. // Make sure you have set a breakpoint in the OutgoingResponse extension were the response for the PlUpload plugin is generated. // The difference to the code above is, that the OutgoingResponse was called before the Pipeline waits for the result of the task. // PipelineExtendedTasks is ultimately the last point in the processing pipeline before the Pipeline returns the results to the client. // context.PipelineControl.TaskManager.PipelineExtendedTasks.Add(t); // Note: we do not have mentioned PipelineCoreTasks so far. This point is reached when pipeline core ends. The core methods responsible // for doing the GET/POST/PUT/DELETE request have done their job, but before the output was generated and the OutgoingResponse extensions // were called. In this demo there would be no difference to Demo 4. If you're using the early extension points (e.g. IncomingRequest) // multiple extension points are between this extension point and the end of pipeline core and may better see the difference. return(false); }
public bool Execute(IBackloadContext context, IStoreFileRequestParam param) { // In this example we make 50 copies of the uploaded file manually. // // Note: You do not need to make copies manually. Backload supports auto copies of uploaded files. // You find the setting for auto copies in the config file (fileSystem: copiesPath). // In this demo Backload is configured to make additional copies within the ~/Files/_Backup folder. string localBackupPath = Helper.GetLocalFilePath(context.Request, param.FileStatusItem.FileUrl); // Helper method in Backload.Contracts to get the full local file path localBackupPath = localBackupPath.Replace("\\Uploads", "\\_Temp"); // Make sure ~/Files/_Temp folder exists // Because we want to post back the new path to the browser, we set the FileUrl property to the new path. // File status item halods the data send back to the client. You can change FileStatusItem.FileData which is the // bayte array to be stored immediately after the extensions finish. param.FileStatusItem.FileUrl = localBackupPath; // There are 5 locations within the processing pipeline to wait for the async operation to be finished: // 1. Use await: // Example: await fileStream.WriteAsync(...); // Info: Waits for the operation to be finished, but does not block the thread. The operating system can do other things. // 2. Use a Task and wait at the end of the extensions handler code (before other extensions are called, if any): // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.ExtensionTask.Add(task); // Info: Finishes the execution of this extension and then waits for the task to be finished (Multiple tasks are supported). // Other extensions whithin this extension point are not executed until this task finishes. // 3. Use a Task and wait at the end of this extension point (all extensions of this extension point are finished): // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.ExtensionPointTasks.Add(task); // Info: Finishes the execution of all extensions within this extension point and then waits for all tasks to be finished. // 4. Use a Task and wait at the end the core pipeline: // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.PipelineCoreTasks.Add(task); // Info: Waits at the end of pipeline core for the task to be finished. This is a point, when the core GET, POST/PUT, DELETE // method has done its job (e.g. saved a file) but before the final output was generated. // 5. Use a Task and wait at the end of the processing pipeline: // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.PipelineExtendedTasks.Add(task); // Info: Waits at the ultimative end of the handler for the task to be finished. This is a point immediately before // the return statement of the processing pipeline (Output for the client browser has been generated already). // Demo 7: In this demo we do our work in a separate thread and do not care about the result (fire&forget strategy). // Because we cannot be sure that the thread finished the work successfully be careful with this approach. // If you rely on the results, use async/await or the task based approach or check if the files exist in another // extension, a subsequent request, a different code or application (e.g.: if (!File.Exits(...)) File.Copy(...)); // Comment out the following line: new System.Threading.Thread(new System.Threading.ThreadStart(new MuchToDo(param.FileStatusItem.FileData, localBackupPath).WriteFiles)).Start(); return(true); // We changed a property, so we set this.Processed to true }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // IMPORTANT NOTE: // Backload v1.9 and above can handle PlUpload internally now. Just send plugin=plupload. You do not need an extension anymore. // // In our example we have a PlUpload Plugin client side, so we transform the output to a PlUpload friendly format. // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // We have two upload plugins in our form, but this extension is only for PlUpload. We have two options to ensure that our // extension only handles PlUpload requests: Convention based handling or conditional handling. // In our example we use handling based on convention, where the convention is: // 1. In our request we set a "plugin" querystring to plupload (plugin=plupload) // 2. The namespace of our extension contains Plugin.PlUpload (not case sensitive) // If you do not want to use convention based handling, just do not send a "plugin" querystring or leave it empty (plugin=) // // You may use conditional handling instead. The extension code is called, and you make the decision based on a condition // if to handle the request or not in your code. Typically you parse the request (Querystring or body) for a condition: // Example: // string control = context.Request.QueryString["control"]; // if ((string.IsNullOrEmpty(control)) || (control != "plupload")) return false; // // Remarks: Don't forget to rebuild your solution if you made changes to your extension, otherwise you may use the old extension assembly. if (param.FileStatus == null) { return(false); // In case something went wrong } if (context.HttpMethod != "DELETE") { var result = new PlUploadFiles(); for (int i = 0; i < param.FileStatus.Files.Count; i++) { var file = param.FileStatus.Files[i]; result.files.Add(new PlUploadFile(file.Success, file.OriginalName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); } param.Result = Helper.Json(result); } else { // Here we use an anonymous type to return some data. PlUpload does not handle server side file deletion, but we made the // ajax request and so we can return whatever we want. var file = param.FileStatus.Files[0]; var result = new { success = file.Success, message = file.ErrorMessage, name = file.FileName }; param.Result = Helper.Json(result); } return(param.FileStatus.Files.Count > 0); // if > 0 we've changed properties. }
public bool Execute(IBackloadContext context, IStoreFileRequestParam param) { // In this example we make 50 copies of the uploaded file manually. // // Note: You do not need to make copies manually. Backload supports auto copies of uploaded files. // You find the setting for auto copies in the config file (fileSystem: copiesPath). // In this demo Backload is configured to make additional copies within the ~/Files/_Backup folder. string localBackupPath = Helper.GetLocalFilePath(context.Request, param.FileStatusItem.FileUrl); // Helper method in Backload.Contracts to get the full local file path localBackupPath = localBackupPath.Replace("\\Uploads", "\\_Temp"); // Make sure ~/Files/_Temp folder exists // Because we want to post back the new path to the browser, we set the FileUrl property to the new path. // File status item halods the data send back to the client. You can change FileStatusItem.FileData which is the // bayte array to be stored immediately after the extensions finish. param.FileStatusItem.FileUrl = localBackupPath; // There are 5 locations within the processing pipeline to wait for the async operation to be finished: // 1. Use await: // Example: await fileStream.WriteAsync(...); // Info: Waits for the operation to be finished, but does not block the thread. The operating system can do other things. // 2. Use a Task and wait at the end of the extensions handler code (before other extensions are called, if any): // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.ExtensionTask.Add(task); // Info: Finishes the execution of this extension and then waits for the task to be finished (Multiple tasks are supported). // Other extensions whithin this extension point are not executed until this task finishes. // 3. Use a Task and wait at the end of this extension point (all extensions of this extension point are finished): // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.ExtensionPointTasks.Add(task); // Info: Finishes the execution of all extensions within this extension point and then waits for all tasks to be finished. // 4. Use a Task and wait at the end the core pipeline: // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.PipelineCoreTasks.Add(task); // Info: Waits at the end of pipeline core for the task to be finished. This is a point, when the core GET, POST/PUT, DELETE // method has done its job (e.g. saved a file) but before the final output was generated. // 5. Use a Task and wait at the end of the processing pipeline: // Example: var task = fileStream.WriteAsync(...); // PipelineControl.TaskManager.PipelineExtendedTasks.Add(task); // Info: Waits at the ultimative end of the handler for the task to be finished. This is a point immediately before // the return statement of the processing pipeline (Output for the client browser has been generated already). // Demo 7: In this demo we do our work in a separate thread and do not care about the result (fire&forget strategy). // Because we cannot be sure that the thread finished the work successfully be careful with this approach. // If you rely on the results, use async/await or the task based approach or check if the files exist in another // extension, a subsequent request, a different code or application (e.g.: if (!File.Exits(...)) File.Copy(...)); // Comment out the following line: new System.Threading.Thread(new System.Threading.ThreadStart(new MuchToDo(param.FileStatusItem.FileData, localBackupPath).WriteFiles)).Start(); return true; // We changed a property, so we set this.Processed to true }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // IMPORTANT NOTE: // Backload v1.9 and above can handle PlUpload internally now. Just send plugin=plupload. You do not need an extension anymore. // // In our example we have a PlUpload Plugin client side, so we transform the output to a PlUpload friendly format. // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // We have two upload plugins in our form, but this extension is only for PlUpload. We have two options to ensure that our // extension only handles PlUpload requests: Convention based handling or conditional handling. // In our example we use handling based on convention, where the convention is: // 1. In our request we set a "plugin" querystring to plupload (plugin=plupload) // 2. The namespace of our extension contains Plugin.PlUpload (not case sensitive) // If you do not want to use convention based handling, just do not send a "plugin" querystring or leave it empty (plugin=) // // You may use conditional handling instead. The extension code is called, and you make the decision based on a condition // if to handle the request or not in your code. Typically you parse the request (Querystring or body) for a condition: // Example: // string control = context.Request.QueryString["control"]; // if ((string.IsNullOrEmpty(control)) || (control != "plupload")) return false; // // Remarks: Don't forget to rebuild your solution if you made changes to your extension, otherwise you may use the old extension assembly. if (param.FileStatus == null) return false; // In case something went wrong if (context.HttpMethod != "DELETE") { var result = new PlUploadFiles(); for (int i = 0; i < param.FileStatus.Files.Count; i++) { var file = param.FileStatus.Files[i]; result.files.Add(new PlUploadFile(file.Success, file.OriginalName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); } param.Result = Helper.Json(result); } else { // Here we use an anonymous type to return some data. PlUpload does not handle server side file deletion, but we made the // ajax request and so we can return whatever we want. var file = param.FileStatus.Files[0]; var result = new { success = file.Success, message = file.ErrorMessage, name = file.FileName }; param.Result = Helper.Json(result); } return (param.FileStatus.Files.Count > 0); // if > 0 we've changed properties. }
public async Task<bool> ExecuteAsync(IBackloadContext context, IGetFilesRequestParam param) { if (param.FileStatus == null) return false; // in the case something went wrong. // In this example we want the user to work with a backup file we created on file upload. // Note: We do not need to use async/await or Tasks in this simple example. We only simulate a long running task. // Important notes: In order to test these demos set breakpoints in all extensions at the beginning of the // ProcessStep/ProcessStepAsync method. After changing the code always rebuild this extension or set a project dependency // for the extension (context menu or Project menu) in your MVC project as we did, so the extension class is automatically // rebuilded. The output build path of the extension is set to the main MVC project in this solution (folder ~/bin/extensions/). // If you do not rebuild the extension, the main MVC application may uses the old extension. // Demo 1: The following line simulates a long running task. We do not care about (await) the results of this task, so it // does not block. The task runs as a background thread within the AppDomains thread pool. You should not use this technique // with file IO, because the thread may be aborted when Backload returns. (If you do not want to await the results of a long // running File I/O operation take a look in the StoreFile extension were we're using a separate thread). var t = Task.Run(() => Task.Delay(System.TimeSpan.FromSeconds(10))); // Demo 2: Uncomment the line below, set breakpoints (also in the GetFilesRequest2 extension) and rebuild the extension. // You'll notice that debugging stops and awaits the result of the task. // The subsequent code and extensions are not executed until the task is finished. But, the ui thread is not blocked and tied up. // You can see this, because the web page comes back. Usually, when ececuting and debug synchronous code, the web page // doesn't come back in long running tasks. // await t; // Demo 3: Comment out the await line above, uncomment the following line, set breakpoints and rebuild the extension. // The debugging now doesn't stop here and the rest of this extensions code (and the extension handling code in the extension manager) // are executed. Before calling the next extension whithin this extension point, the extension manager awaits the result. Note that // the second extension below isn't called until the task is finished. // context.PipelineControl.TaskManager.ExtensionTasks.Add(t); // Demo 4: Comment out the ExtensionTasks.Add line above, uncomment the following line, set breakpoints and rebuild the extension. // When debugging the application you'll see, that the second extension also can execute and finish its code, before the // extension manager awaits at the end of the extension point (before code execution returns to the pipeline) the result. // context.PipelineControl.TaskManager.ExtensionPointTasks.Add(t); // Demo 5: Comment out the ExtensionPointTasks.Add line above, uncomment the following line, set breakpoints and rebuild the extension. // Make sure you have set a breakpoint in the OutgoingResponse extension were the response for the PlUpload plugin is generated. // The difference to the code above is, that the OutgoingResponse was called before the Pipeline waits for the result of the task. // PipelineExtendedTasks is ultimately the last point in the processing pipeline before the Pipeline returns the results to the client. // context.PipelineControl.TaskManager.PipelineExtendedTasks.Add(t); // Note: we do not have mentioned PipelineCoreTasks so far. This point is reached when pipeline core ends. The core methods responsible // for doing the GET/POST/PUT/DELETE request have done their job, but before the output was generated and the OutgoingResponse extensions // were called. In this demo there would be no difference to Demo 4. If you're using the early extension points (e.g. IncomingRequest) // multiple extension points are between this extension point and the end of pipeline core and may better see the difference. return false; }
// This extension is only for demo purposes, as we throw an exception here // Don't forget to rebuid your solution if you made changes to your extension, otherwise you may use the old extension assembly. public bool Execute(IBackloadContext context, IIncomingRequestParam param) { if (context.HttpMethod == "POST") { for (int i = 0; i < context.Request.Files.Count; i++) { var file = context.Request.Files[i]; if (file.FileName.ToLower().Contains("badfile")) { context.PipelineControl.Message.MessageText = "Bad file name."; throw new Exception(context.PipelineControl.Message.MessageText); } } } return false; // No properties have been changed, so we set return false. }
// This extension is only for demo purposes, as we throw an exception here // Don't forget to rebuid your solution if you made changes to your extension, otherwise you may use the old extension assembly. public bool Execute(IBackloadContext context, IIncomingRequestParam param) { if (context.HttpMethod == "POST") { for (int i = 0; i < context.Request.Files.Count; i++) { var file = context.Request.Files[i]; if (file.FileName.ToLower().Contains("badfile")) { context.PipelineControl.Message.MessageText = "Bad file name."; throw new Exception(context.PipelineControl.Message.MessageText); } } } return(false); // No properties have been changed, so we set return false. }
public async Task<bool> ExecuteAsync(IBackloadContext context, IGetFilesRequestParam param) { if (param.FileStatus == null) return false; // if cace something went wrongt. // We use this extension only to demonstrate you different examples of blocked and parallel requests. // IMPORTANT NOTE: The FileStatusSimple here is only to show, that you can create your FileStatus from scratch. // You may use FileStatusSimple as we did here or implement your own iFileUploadStatus class if you need it. // In this example it'll be sufficient to simply set the file.FileUrl of the FileStatus property, no need to use FileStatusSimple. FileStatusSimple status = new FileStatusSimple(); foreach (var file in param.FileStatus.Files) { file.FileUrl = file.FileUrl.Replace("/Uploads", "/_Backup"); status.Files.Add(file); } param.FileStatus = status; return (param.FileStatus.Files.Count > 0); // if FileStatus.files.Count > 0 we've processed the response, so we need notify the pipeline }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // Don't forget to rebuid the extension if you made changes to your extension, otherwise you may use the old extension assembly. if (context.RequestType == RequestType.Default) { var result = new PlUploadFiles(); foreach (var file in param.FileStatus.Files) { if (context.HttpMethod != "DELETE") result.files.Add(new PlUploadFile(file.Success, file.FileName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); else result.files.Add(new PlUploadFileCore(file.Success, file.FileName, file.ErrorMessage)); //We only return values we need on client side, so we return a PlFileCode object } param.Result = Helper.Json(result); return (param.FileStatus.Files.Count > 0); // we have processed the response, so we notify the pipeline } return false; }
public async Task <bool> ExecuteAsync(IBackloadContext context, IGetFilesRequestParam param) { if (param.FileStatus == null) { return(false); // if cace something went wrongt. } // We use this extension only to demonstrate you different examples of blocked and parallel requests. // IMPORTANT NOTE: The FileStatusSimple here is only to show, that you can create your FileStatus from scratch. // You may use FileStatusSimple as we did here or implement your own iFileUploadStatus class if you need it. // In this example it'll be sufficient to simply set the file.FileUrl of the FileStatus property, no need to use FileStatusSimple. FileStatusSimple status = new FileStatusSimple(); foreach (var file in param.FileStatus.Files) { file.FileUrl = file.FileUrl.Replace("/Uploads", "/_Backup"); status.Files.Add(file); } param.FileStatus = status; return(param.FileStatus.Files.Count > 0); // if FileStatus.files.Count > 0 we've processed the response, so we need notify the pipeline }
public bool Execute(IBackloadContext context, IOutgoingResponseParam param) { // Note: We use convention based plugin handling (see Example 09). // Backload v1.9 and above can handle this internally. Just send plugin=plupload in the querystring or in the config file. // // In our example we have a PlUpload Plugin client side, so we transform the output to a PlUpload friendly format. // Important Note: Since Version 1.9 PlUpload can be handled internally (Plugin attribute in the configuration). // // Remarks: Don't forget to rebuild your solution if you made changes to your extension, otherwise you may use the old extension assembly. if (param.FileStatus == null) return false; // in the case something went wrong. var result = new PlUploadFiles(); foreach (var file in param.FileStatus.Files) { if (context.HttpMethod != "DELETE") result.files.Add(new PlUploadFile(file.Success, file.FileName, file.FileSize, file.ContentType, file.DeleteUrl, file.ThumbnailUrl, file.FileUrl, file.ErrorMessage)); else result.files.Add(new PlUploadFileCore(file.Success, file.FileName, file.ErrorMessage)); //We only return values we need on client side, so we instatiate a PlFileCore object } param.Result = Helper.Json(result); return true; // We've processed the response }
public bool Execute(IBackloadContext context, IDeleteFilesRequestParam param) { // In this extension we delete the copies we've created within the _Backup and _Temp folder. // Backload has made the copies within the _Backup folder automatically, because it is configured to do so (config file) // The copies within the _Temp folder were created for demo purposes in the StoreFileRequest extension. string dirBackup = string.Empty; string dirTemp = string.Empty; if (param.DeleteFiles == null) { return(false); // in the case something went wrong. } if (param.DeleteFiles.Files.Count > 0) { dirTemp = Path.GetDirectoryName(Helper.GetLocalFilePath(context.Request, param.DeleteFiles.First().FileUrl).Replace("\\Uploads", "\\_Temp")); // Helper method in Backload.Contracts to get the full local file path dirBackup = Path.GetDirectoryName(Helper.GetLocalFilePath(context.Request, param.DeleteFiles.First().FileUrl).Replace("\\Uploads", "\\_Backup")); // Helper method in Backload.Contracts to get the full local file path } foreach (var file in param.DeleteFiles.Files) { // Synchronous deletion: // Delete the backup file Backload created (see config file) string pathBackupFile = Path.Combine(dirBackup, file.FileName); if (File.Exists(pathBackupFile)) { File.Delete(pathBackupFile); } // Asynchronous deletion with Tasks: // Demo 6: Running and awaiting a method asynchronously in a thread of the AppDomains thread pool. // Note: We do not use await here, because we have implemented the synchronous interface. // The Tasks will be awaited at the end of the extension point for the FilesDelete extension. If we do not await the // result, the task may be aborted and does not finish file deletion. var t = Task.Run(() => DeleteTempFiles(dirTemp, file.FileName)); context.PipelineControl.TaskManager.ExtensionPointTasks.Add(t); } return(param.DeleteFiles.Files.Count > 0); }