NSubstitute HttpPostedFileBase SaveAs - asp.net

On my journey of unit testing my code and I have this code:
var ufile = Substitute.For<HttpPostedFileBase>();
var server = Substitute.For<HttpServerUtilityBase();
var saved = false;
ufile.FileName.Returns("somefileName");
var fileName = fs.Path.GetFileName(ufile.FileName);
var path = fs.Path.Combine(server.MapPath(upath), fileName);
ufile.When(h => h.SaveAs(path)).Do(x => saved = true);
Assert.IsTrue(saved);
So here is what I am testing which I gleaned from different sites:
public ActionResult UploadFiles()
{
var fileinfo = new List<UploadedImageViewModel>();
foreach (string files in Request.Files)
{
var hpf = Request.Files[files] as HttpPostedFileBase; //
if (hpf != null && hpf.ContentLength > 0)
continue;
var FileName = Path.GetFileName(hpf.FileName); //Gets name of file uploaded
var temppath = Path.Combine(Server.MapPath("~/uploadtemp/"), FileName); // creates a string representation of file location
hpf.SaveAs(temppath);
//resize the image before you save it to the right folder under FileUploads
}
return View(fileinfo);
}
Can someone please help me understand this when().Do() syntax of Nsubstitute? In the docs, it says the do should have an action in it but I will need examples to understand.
Then the SaveAs() method of HttpPostedFileBase is void and in Nsubstitute Docs, it says to use when().Do() for void methods so please tell me what is wrong with my unit test.

//Suppose we have this setup
public class MyClass
{
string ReturnSomething()
{
return "FooBar";
}
void DoSomething(out string reason){
reason = 'Oops';
}
}
The usual stubbing syntax for NSubstitute is to use Returns like this:
myClass.ReturnSomething().Returns("wibble");
This stubs out ReturnSomething(), but the Returns syntax only works for methods with a return value.
For methods that have no return we can instead use When().Do(). This is basically what is meant by an Action in their documentation (as opposed to a Func, which does have a return value). A common need to do this is to fill in output parameters on such methods:
string reason;
myClass.When(c => c.DoSomething(out reason))
.Do(args =>
{
args[0] = "Fail";
});
For more on Action and Func see MSDN: Action, Func.
In the specific case of your unit test, instead of setting a variable saved when SaveAs is invoked, consider asserting using the NSubstitute.Received construct instead.

Related

Download multiple files (50mb) blazor server-side

i can't really find a way to download a 100mb zip file from the server to the client and also show the progress while downloading. So how will this look for a normal api controller i can add to my server-side project? if lets say i have 3 files i want to download at 50mb each.
i have tried using JSInterop like this, but this is not showing the progress of the file download, and how will i do if i want to download 3 seperate files at the same time?
try
{
//converting file into bytes array
var dataBytes = System.IO.File.ReadAllBytes(file);
await JSRuntime.InvokeVoidAsync(
"downloadFromByteArray",
new
{
ByteArray = dataBytes,
FileName = "download.zip",
ContentType = "application/force-download"
});
}
catch (Exception)
{
//throw;
}
JS:
function downloadFromByteArray(options: {
byteArray: string,
fileName: string,
contentType: string
}): void {
// Convert base64 string to numbers array.
const numArray = atob(options.byteArray).split('').map(c => c.charCodeAt(0));
// Convert numbers array to Uint8Array object.
const uint8Array = new Uint8Array(numArray);
// Wrap it by Blob object.
const blob = new Blob([uint8Array], { type: options.contentType });
// Create "object URL" that is linked to the Blob object.
const url = URL.createObjectURL(blob);
// Invoke download helper function that implemented in
// the earlier section of this article.
downloadFromUrl({ url: url, fileName: options.fileName });
// At last, release unused resources.
URL.revokeObjectURL(url);
}
UPDATE:
if im using this code, it will show me the progress of the file. But how can i trigger it from my code? This way does not do it. But typing the url does.
await Http.GetAsync($"Download/Model/{JobId}");
Controller
[HttpGet("download/model/{JobId}")]
public IActionResult DownloadFile([FromRoute] string JobId)
{
if (JobId == null)
{
return BadRequest();
}
var FolderPath = $"xxxx";
var FileName = $"Model_{JobId}.zip";
var filePath = Path.Combine(environment.WebRootPath, FolderPath, FileName);
byte[] fileBytes = System.IO.File.ReadAllBytes(filePath);
return File(fileBytes, "application/force-download", FileName);
}
UPDATE 2!
i have got it download with progress and click with using JSInterop.
public async void DownloadFiles()
{
//download all selectedFiles
foreach (var file in selectedFiles)
{
//download these files
await JSRuntime.InvokeAsync<object>("open", $"Download/Model/{JobId}/{file.Name}", "_blank");
}
}
Now the only problem left is.. it only downloads the first file out of 3.

Generating PDFs using Phantom JS on .NET applications

I have been looking into phantomJS and looks like it could be a great tool to use generating PDFs. I wonder if anyone have successfully used it for their .NET applications.
My specific question is: how would you use modules like rasterize.js on the server, receive requests and send back generated pdfs as a response.
My general question is: is there any best practice for using phantomJS with .NET Applications. What would be the best way to achieve it?
I am fairly new in .NET World and I would appreciate the more detailed answers. Thanks everyone. :)
I don't know about best practices, but, I'm using phantomJS with no problems with the following code.
public ActionResult DownloadStatement(int id)
{
string serverPath = HttpContext.Server.MapPath("~/Phantomjs/");
string filename = DateTime.Now.ToString("ddMMyyyy_hhmmss") + ".pdf";
new Thread(new ParameterizedThreadStart(x =>
{
ExecuteCommand("cd " + serverPath + #" & phantomjs rasterize.js http://localhost:8080/filetopdf/" + id.ToString() + " " + filename + #" ""A4""");
})).Start();
var filePath = Path.Combine(HttpContext.Server.MapPath("~/Phantomjs/"), filename);
var stream = new MemoryStream();
byte[] bytes = DoWhile(filePath);
return File(bytes, "application/pdf", filename);
}
private void ExecuteCommand(string Command)
{
try
{
ProcessStartInfo ProcessInfo;
Process Process;
ProcessInfo = new ProcessStartInfo("cmd.exe", "/K " + Command);
ProcessInfo.CreateNoWindow = true;
ProcessInfo.UseShellExecute = false;
Process = Process.Start(ProcessInfo);
}
catch { }
}
public ViewResult FileToPDF(int id)
{
var viewModel = file.Get(id);
return View(viewModel);
}
private byte[] DoWhile(string filePath)
{
byte[] bytes = new byte[0];
bool fail = true;
while (fail)
{
try
{
using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
{
bytes = new byte[file.Length];
file.Read(bytes, 0, (int)file.Length);
}
fail = false;
}
catch
{
Thread.Sleep(1000);
}
}
System.IO.File.Delete(filePath);
return bytes;
}
Here is the action flow:
The user clicks on a link to DownloadStatement Action. Inside there, a new Thread is created to call the ExecuteCommand method.
The ExecuteCommand method is responsible to call phantomJS. The string passed as an argument do the following.
Go to the location where the phantomJS app is and, after that, call rasterize.js with an URL, the filename to be created and a print format. (More about rasterize here).
In my case, what I really want to print is the content delivered by the action filetoupload. It's a simple action that returns a simple view. PhantomJS will call the URL passed as parameter and do all the magic.
While phantomJS is still creating the file, (I guess) I can not return the request made by the client. And that is why I used the DoWhile method. It will hold the request until the file is created by phantomJS and loaded by the app to the request.
If you're open to using NReco.PhantomJS, which provides a .NET wrapper for PhantomJS, you can do this very succinctly.
public async Task<ActionResult> DownloadPdf() {
var phantomJS = new PhantomJS();
try {
var temp = Path.Combine(Path.GetTempPath(),
Path.ChangeExtension(Path.GetRandomFileName(), "pdf")); //must end in .pdf
try {
await phantomJS.RunAsync(HttpContext.Server.MapPath("~/Scripts/rasterize.js"),
new[] { "https://www.google.com", temp });
return File(System.IO.File.ReadAllBytes(temp), "application/pdf");
}
finally {
System.IO.File.Delete(temp);
}
}
finally {
phantomJS.Abort();
}
}
Here's some very basic code to generate a PDF using Phantom.JS but you can find more information here: https://buttercms.com/blog/generating-pdfs-with-node
var webPage = require('webpage');
var page = webPage.create();
page.viewportSize = { width: 1920, height: 1080 };
page.open("http://www.google.com", function start(status) {
page.render('google_home.pdf, {format: 'pdf', quality: '100'});
phantom.exit();
});

How to pass content in response from Exception filter in Asp.net WebAPI?

Consider following code:
My problem is:
1) I can't seem to cast the errors to HttpContent
2) I can't use the CreateContent extension method as this doesn't exist on the context.Response.Content.CreateContent
The example here only seems to provide StringContent and I'd like to be able to pass the content as a JsobObject:
http://www.asp.net/web-api/overview/web-api-routing-and-actions/exception-handling
public class ServiceLayerExceptionFilter : ExceptionFilterAttribute
{
public override void OnException(HttpActionExecutedContext context)
{
if (context.Response == null)
{
var exception = context.Exception as ModelValidationException;
if ( exception != null )
{
var modelState = new ModelStateDictionary();
modelState.AddModelError(exception.Key, exception.Description);
var errors = modelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage);
// Cannot cast errors to HttpContent??
// var resp = new HttpResponseMessage(HttpStatusCode.BadRequest) {Content = errors};
// throw new HttpResponseException(resp);
// Cannot create response from extension method??
//context.Response.Content.CreateContent
}
else
{
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
}
}
base.OnException(context);
}
}
context.Response = new HttpResponseMessage(context.Exception.ConvertToHttpStatus());
context.Response.Content = new StringContent("Hello World");
you also have the possibility to use the CreateResponse (added in RC to replace the generic HttpResponseMessage<T> class that no longer exists) method if you want to pass complex objects:
context.Response = context.Request.CreateResponse(
context.Exception.ConvertToHttpStatus(),
new MyViewModel { Foo = "bar" }
);

Having problems saving and or returning\displaying a valid image file using FileContentResult and HttpPostedFileBase

If I call the GetImage action method directly ie: localserver/admin/GetImage?ProductID=36 it prompts me to download the file. Upon download, even when the file has the correct extension it does not open, testing with regular *.jpeg images from my pictures folder.
Also how do I set the filename when saving the file to database, do I need to create additional varchar type etc. field in database just for the filename ?
Once assigned to productMedia.ImageData the data is also all zeroes, hence null. Unlike the "image", which has varying digits in there. Maybe it should be image.InputStream.Write and not image.InputStream.Read ? Tried it but still no go.
View
#using (Html.BeginForm("SaveProduct", "Admin", FormMethod.Post, new { enctype = "multipart/form-data" }))
Here's the saving procedure:
if (image != null)
{
var ProductMedia = new ProductMedia();
ProductMedia.ImageMimeType = image.ContentType;
ProductMedia.ImageData = new byte[image.ContentLength];
image.InputStream.Read(ProductMedia.ImageData, 0, image.ContentLength);
ProductMedia.ProductID = product.ProductID;
context.ProductMedias.AddObject(ProductMedia);
context.SaveChanges();
}
and the GetImage method:
public FileContentResult GetImage(int productID)
{
var ProductImages = context.ProductMedias.FirstOrDefault(x => x.ProductID == productID);
if (ProductImages != null)
{
return File(ProductImages.ImageData, ProductImages.ImageMimeType);
}
else
{
return null;
}
}
Nothing wrong with your GetImage method check your saving procedure for correct content type correct data. And in database check whether you have enough size for the image in ImageData column.
For the second question , Yes you need a separate column for file name.
Here's the syntax that should be used and it works.
if (image != null)
{
var product = new Product();
product.FileName = image.FileName; //<- optional filename
product.ImageMimeType = image.ContentType;
int length = image.ContentLength;
byte[] buffer = new byte[length];
image.InputStream.Read(buffer, 0, length);
product.ImageData = buffer;
//Save product to database
}

URLStream throws Error#2029 in my flex AIR app

In my AIR app, I am trying to implement a file downloader using URLStream.
public class FileDownloader {
// Class to download files from the internet
// Function called every time data arrives
// called with an argument of how much has been downloaded
public var onProgress :Function = function(loaded:Number, total:Number):void{};
public var onComplete :Function = function():void{};
public var remotePath :String = "";
public var localFile :File = null;
public var running:Boolean = false;
public var stream :URLStream;
private var fileAccess :FileStream;
public function FileDownloader( remotePath :String = "" , localFile :File = null ) {
this.remotePath = remotePath;
this.localFile = localFile;
}
public function load() :void
{
try
{
stream = null;
if( !stream || !stream.connected )
{
stream = new URLStream();
fileAccess = new FileStream();
var requester :URLRequest = new URLRequest( remotePath );
var currentPosition :uint = 0;
var downloadCompleteFlag :Boolean = false;
// Function to call oncomplete, once the download finishes and
// all data has been written to disc
fileAccess.addEventListener( "outputProgress", function ( result ):void {
if( result.bytesPending == 0 && downloadCompleteFlag ) {
stream.close();
fileAccess.close();
running = false;
onComplete();
}
});
fileAccess.openAsync( localFile, FileMode.WRITE );
fileAccess.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent)
{
trace('remotePath: '+remotePath);
trace('io error while wrintg ....'+e.toString());
});
stream.addEventListener(IOErrorEvent.IO_ERROR, function(e:IOErrorEvent)
{
trace('remotePath: '+remotePath);
trace('There was an IO error with the stream: '+e.text);
});
stream.addEventListener( "progress" , function (e:ProgressEvent) :void {
var bytes :ByteArray = new ByteArray();
var thisStart :uint = currentPosition;
currentPosition += stream.bytesAvailable;
// ^^ Makes sure that asyncronicity does not break anything
try
{
//trace('reading from '+remotePath+' ...');
stream.readBytes( bytes, thisStart );
fileAccess.writeBytes( bytes, thisStart );
}
catch(err:Error)
{
trace('remotePath: '+remotePath);
trace('error while writing bytes from...'+err.name+':'+err.message);
if(stream.connected)
stream.close();
abort();
onComplete();
return;
}
onProgress( e.bytesLoaded, e.bytesTotal );
});
stream.addEventListener( "complete", function () :void {
downloadCompleteFlag = true;
});
stream.load( requester );
} else {
// Do something unspeakable
}
running = true;
}
catch(err:Error)
{
trace('error while downloading the file: '+err);
}
}
public function abort():void {
try {
stream.close();
trace('stream closed');
running = false;
}
catch(err:Error) {
trace('error while aborting download');
trace(err);
}
}
}
I simply create an object of the above class and passing the url and the file and call the load function. For some files I get the following error.
remotePath: http://mydomain.com/238/6m_608-450.jpg
error while writing bytes from...Error:Error #2029: This URLStream object does not have a stream opened.
Which means the error is from the file stream(fileAccess) that I am using. I am unable to figure out why this could be happening. If I try to open the url http://mydomain.com/238/6m_608-450.jpg in the browser, it opens properly. This happens randomly for some files. What could be the problem?
I have tried in my office and it works for me (for differents files and filesize).
So, can you describe the files (or types files) which don't work for you (post an url if you can) ?
I would say that when you use the method readBytes your stream (so the URLStream) is ever close.
More, I allows me some advice :
1/ Use flash's constants instead of simple string
2/ Don't forget to remove your listeners once the operation completed
3/ Your method FileDownloader is quite confusing. Use lowercase if it's a function or puts a capital letter with class's name if you use it as a constructor. For me, this function must be a constructor.

Resources