.NET Nancy response with a video file (self hosting) - c#

I am writing a self-hosting server on .NET based on REST architecture with Nancy(version 1.4.4). I preferred to do self-hosting(Nancy.Hosting.Self is version 1.4.1) and one of requires functionalities is to respond for a request with a video file. To make picture clear, my partner writes React application and he needs this video.
I've tried different options:
First I tried Response.AsFile() but when I try to access it by a link, I get 404 with label "The resource you have requested cannot be found." and I have no idea why...
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
return Response.AsFile(#"C:\7\video112018.mp4","video/mp4");
};
}
}
Second variant was to use GenericFileResponce like in the code below, but it leads to the same problem:
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
GenericFileResponse fileResponse = new GenericFileResponse(#"C:\7\video112018.mp4");
return fileResponse;
};
}
}
Last option I tried was to write directly to response stream like in the code below, but in this case an error "The specified network name is no longer available" occurs. And what makes it tricky is that this error occurs sometimes but I didn't find any dependency where it comes from...
public class HelloModule : NancyModule
{
public HelloModule()
{
Get["/"] = parameters =>
{
return new Response
{
ContentType = "video/mp4",
Contents = s =>
{
String fileName = #"C:\7\video112018.mp4";
using (var stream = new FileStream(fileName, FileMode.Open))
stream.CopyTo(s);
s.Flush();
s.Close();
}
};
};
}
}
I would really appreciate if you have any suggestions about these solutions or give another one.
P. S. I also tried to send an image, it works with third approach but not with first or second
P.P. S. don't judge the code strictly, because it's only an example)

I was able to do very basic streaming of video files by doing this:
Get["/"] = p =>
{
var file = new FileStream(#"PATH_TO_FILE", FileMode.Open);
return new StreamResponse(() => file, "video/mp4");
}
This allowed video files to play, but there was no seeking.
In the end I found this post. Adding these extensions allows for the video to be seeked.

Related

How to push LSP Diagnostic using OmniSharp LanguageServer?

I'm using OmniSharp's C# LSP server to implement a simple parsing/language service for a VS Code plugin. I've managed to get the basics up and running, but I've not been able to figure out how to push diagnostic messages to VS Code (like in this typescript sample).
Does anyone have any sample code/hints that would be of use?
Thanks!
Having spoken with #david-driscoll, it turns out I needed to stash a reference to ILanguageServerFacade in my constructor and use the PublishDiagnostics extension method on TextDocument. Ie:
public class TextDocumentSyncHandler : ITextDocumentSyncHandler
{
private readonly ILanguageServerFacade _facade;
public TextDocumentSyncHandler(ILanguageServerFacade facade)
{
_facade = facade;
}
public Task<Unit> Handle(DidChangeTextDocumentParams request, CancellationToken cancellationToken)
{
// Parse your stuff here
// Diagnostics are sent a document at a time, this example is for demonstration purposes only
var diagnostics = ImmutableArray<Diagnostic>.Empty.ToBuilder();
diagnostics.Add(new Diagnostic()
{
Code = "ErrorCode_001",
Severity = DiagnosticSeverity.Error,
Message = "Something bad happened",
Range = new Range(0, 0, 0, 0),
Source = "XXX",
Tags = new Container<DiagnosticTag>(new DiagnosticTag[] { DiagnosticTag.Unnecessary })
});
_facade.TextDocument.PublishDiagnostics(new PublishDiagnosticsParams()
{
Diagnostics = new Container<Diagnostic>(diagnostics.ToArray()),
Uri = request.TextDocument.Uri,
Version = request.TextDocument.Version
});
return Unit.Task;
}
}
For real code, you would want a centralised array of Diagnostic objects, but this shows the basics of how to get it done.
Thank you David!

How to make a POST method for an image?

So I've been working on this API and I didn't have issues until now when I tried figuring out how to make a POST method that takes an image as a parameter. In theory, this is how it should work:
From a web page, you will upload an image and then using the route to the API, the image information will be sent to the database like this:
Now, I've been searching for an answer to this on different several pages but none of them actually helped. In fact, the only guides that I've found were about integrating that method into a Web Api (see https://www.c-sharpcorner.com/article/uploading-image-to-server-using-web-api-2-0/) but this doesn't really work for me as I can't inherit some of the methods in my solution. For example, using the link above, I had issues with HttpContext.Current, and I would guess that is because of the solution that I am currently using. However, that's another question to be asked.
So my methods look pretty much like this:
public class RecipeController : Controller
{
private readonly Func<SqlConnection> _connFactory;
public RecipeController(Func<SqlConnection> connFactory)
{
_connFactory = connFactory;
}
[Route("v1/recipe/{recipeBy}")]
[HttpGet()]
public List<Recipe> GetRecipes(string recipeBy)
{
using (var con = _connFactory())
{
con.Open();
return con.Query<Recipe>("SELECT * FROM dbo.Recipe WHERE RecipeBy = #recipeBy", new { recipeBy }).ToList();
}
}
....
I am using Dapper to pass the values to the database.
Therefore, my question is: How can I write a POST method that takes an uploaded image as a parameter and then passes it to the database? I do realize that this question is pretty vague, as I didn't even provide reproducible code. The thing is, until now I didn't even figure out a correct way to start working on, so I couldn't really provide any useful code that you can help me with. Any tips, hints, advice, tutorials... Anything is welcome!
You can accomplish this by using the Html5 FileReader class to read the image to a string on the client-side and then post this to the api end-point:
function UploadImage()
{
var file = input[0].files[0];
var reader = new FileReader();
reader.onloadend = function () {
var imageDataString = reader.result;
// post the fileString value to your api here
}
if (file) {
reader.readAsDataURL(file);
}
}
Then on your Controller you would convert the Base64String into a byte[] and store it in your db:
if (imageDataString != null && imageDataString != String.Empty)
{
string imageDataParsed = imageDataString.Substring(imageDataString.IndexOf(',') + 1);
byte[] imageBytes = Convert.FromBase64String(imageDataParsed);
}

gsapi_init_with_args is made: -100

I'm trying to build a PostScript to PDF Converter using Ghostscript.Net.
The Args that GetArgs return, are the ones I usually use to call gswin32c.exe and they work fine.
But every time i call Process, i get an error Saying "An error occured when call to 'gsapi_init_with_args' is made: -100". Googling that error didn't bring anything up so I thought I might ask here.
Are there differnet arguments to consider when calling the .dll directly with Ghostscript.net? Or did I made a mistake somewhere else?
Here's my class:
public class PdfConverter
{
#region Private Fields
private List<GhostscriptVersionInfo> _Versions = GhostscriptVersionInfo.GetInstalledVersions(GhostscriptLicense.GPL | GhostscriptLicense.AFPL | GhostscriptLicense.Artifex);
#endregion
#region Private Properties
private GhostscriptVersionInfo Version { get; set; }
#endregion
#region Construction
public PdfConverter()
{
Version = GhostscriptVersionInfo.GetLastInstalledVersion();
}
#endregion
#region Public Members
public bool ConvertToPdf(DirectoryInfo dir)
{
var d = dir;
if(!d.Exists)
return false;
var postScriptFiles = d.GetFiles("*.ps");
var pdfFiles = postScriptFiles.Select(psf => new FileInfo(Path.ChangeExtension(psf.FullName, ".pdf")));
foreach(var file in postScriptFiles) {
//ThreadPool.QueueUserWorkItem(new WaitCallback((o) => {
Process(file, new FileInfo(Path.ChangeExtension(file.FullName, ".pdf")));
//}));
}
pdfFiles.ForEach(pdf => pdf?.Refresh());
return pdfFiles.All(pdf => pdf.Exists);
}
#endregion
#region Private Helpers
private void Process(FileInfo inputFile, FileInfo outputFile)
{
Console.WriteLine($"Converting {inputFile} to {outputFile}");
var proc = new GhostscriptProcessor(Version, true);
proc.Process(GetArgs(inputFile, outputFile).ToArray(), new ConsoleStdIO(true, true, true));
}
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-q ",
$"-sDEVICE=pdfwrite",
$"-dSAFER",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
$"-dEmbedAllFonts=true",
$"-dAutoRotatePages=/None",
$"-sOutputFile=\"{outputFile.FullName}\"",
$"-dCompatibilityLevel#1.4",
$"-c .setpdfwrite",
$"-f \"{inputFile.FullName}\""
};
}
#endregion
}
Edit:
I forgot to mention: To implement it i had to make my own GhostsdcriptStdIO class. I admit that I'm not entirely sure if I did this right. Although it does get instanciated without exceptions, and override StdOut(...) get's called, and the output is written to the console as expected. override void StdError(...) get's called as well. And also written to the console as expeted.
The Output of the error btw is:
"**** Could not open the file "c:\temp\test.pdf""
"**** Unable to open the initial device, quitting."
Here's my ConsoleStdIO class:
public class ConsoleStdIO : Ghostscript.NET.GhostscriptStdIO
{
#region Construction
public ConsoleStdIO(bool handleStdIn, bool handleStdOut, bool handleStdError) : base(handleStdIn, handleStdOut, handleStdError) { }
#endregion
#region Overrides
public override void StdError(string error)
{
var foo = Encoding.Default.GetBytes(error);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
public override void StdIn(out string input, int count)
{
byte[] bytes = new byte[0];
using(var stdInput = Console.OpenStandardInput()) {
stdInput.Read(bytes, 0, count);
}
input = Encoding.Default.GetString(bytes);
}
public override void StdOut(string output)
{
var foo = Encoding.Default.GetBytes(output);
var lenght = foo.Length;
using (var err = Console.OpenStandardError()) {
if(err.CanWrite)
err.Write(foo, 0, lenght);
}
}
#endregion
}
Again: doing the same operation with the exact same files and arguments using gswin32c.exe works fine.
Happy Hacking
Error -100 is gs_error_Fatal, which means 'something catastrophic went wrong'. Its an indication that the program failed to start up properly and we can't tell why. The back channel may contain more information.
And indeed, the back channel tells you what's wrong:
**** Could not open the file "c:\temp\test.pdf
**** Unable to open the initial device, quitting.
Ghostscript is unable to open the output file, which means it can't open the pdfwrite device (because that requires an output file) so it aborts the operation.
There could be a number of reasons why Ghostscript can't open the output file. The first thing I'd do is trim down the number of arguments;
You don't want -q (quiet) when you are trying to debug a problem, you want all the information you can get.
I'd remove -dSAFER at least to start with, because that prevents Ghostscript accessing directories outside the current working directory and certain 'special' ones. It may well prevent you accessing the temp directory.
You don't need to set EmbedAllFonts when its the same value as the default.
You could drop the CompatibilityLevel (and note that you've used a # there instead of an =) switch, and the AutoRotatePages while getting this to work.
The "-c .setpdfwrite -f" string has been pointless for years but people still keep using it. All that does these days is slow down the start of processing, ditch it.
Finally you can try changing the backslash ('\') characters to forward slash ('/') in case your string handling is messing that up, or use double backslashes (I'd use the forward slash myself).
You should also check that c:\test\temp.pdf doesn't exist, or if it does exist is not read-only or already open in a different application.
So I solved the problem...
After taking KenS' advice I could run the application without Ghostscript (not Ghostscript.NET) giving me any errors. But it did not produce an actual PDF File.
So KenS's answer did not quite solve the problem, but since 'less is more' and since he took the time to talk to me on IRC to verify that my args in itself were correct, I'll give the answer points nonetheless.
What actually solved my was the following:
Here my original GetArgs(...)
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}
Someone in #csharp pointed out to me, that in c, the first argument is always the name of the command. So he suggested to just put "gs" as the first argument (as a dummy) and try... And that's what actually solved my problem.
So this is how the working GetArgs(...) looks:
private IEnumerable<string> GetArgs(FileInfo inputFile, FileInfo outputFile)
{
return new [] {
$"gs",
$"-sDEVICE=pdfwrite",
$"-dNOPAUSE",
$"-dBATCH",
$"-sPAPERSIZE=a4",
#"-sFONTPATH=" + System.Environment.GetFolderPath(System.Environment.SpecialFolder.Fonts),
$"-sOutputFile={outputFile.FullName}",
$"{inputFile.FullName}",
};
}

Get access to the URL's being used in System.Web.Optimization

Background: I'm using the HTML 5 Offline App Cache and dynamically building the manifest file. Basically, the manifest file needs to list each of the static files that your page will request. Works great when the files are actually static, but I'm using Bundling and Minification in System.Web.Optimization, so my files are not static.
When in the DEBUG symbol is loaded (i.e. debugging in VS) then the actual physical files are called from the MVC View. However, when in Release mode, it calls a virtual file that could look something like this: /bundles/scripts/jquery?v=FVs3ACwOLIVInrAl5sdzR2jrCDmVOWFbZMY6g6Q0ulE1
So my question: How can I get that URL in the code to add it to the offline app manifest?
I've tried:
var paths = new List<string>()
{
"~/bundles/styles/common",
"~/bundles/styles/common1024",
"~/bundles/styles/common768",
"~/bundles/styles/common480",
"~/bundles/styles/frontend",
"~/bundles/scripts/jquery",
"~/bundles/scripts/common",
"~/bundles/scripts/frontend"
};
var bundleTable = BundleTable.Bundles;
foreach (var bundle in bundleTable.Where(b => paths.Contains(b.Path)))
{
var bundleContext = new BundleContext(this.HttpContext, bundleTable, bundle.Path);
IEnumerable<BundleFile> files = bundle.GenerateBundleResponse(bundleContext).Files;
foreach (var file in files)
{
var filePath = file.IncludedVirtualPath.TrimStart(new[] { '~' });
sb.AppendFormat(formatFullDomain, filePath);
}
}
As well as replacing GenerateBundleResponse() with EnumerateFiles(), but it just always returns the original file paths.
I'm open to alternative implementation suggestions as well. Thanks.
UPDATE: (7/7/14 13:45)
As well as the answer below I also added this Bundles Registry class to keep a list of the required static files so that it works in debug mode in all browsers. (See comments below)
public class Registry
{
public bool Debug = false;
public Registry()
{
SetDebug();
}
[Conditional("DEBUG")]
private void SetDebug()
{
Debug = true;
}
public IEnumerable<string> CommonScripts
{
get
{
if (Debug)
{
return new string[]{
"/scripts/common/jquery.validate.js",
"/scripts/common/jquery.validate.unobtrusive.js",
"/scripts/common/knockout-3.1.0.debug.js",
"/scripts/common/jquery.timepicker.js",
"/scripts/common/datepicker.js",
"/scripts/common/utils.js",
"/scripts/common/jquery.minicolors.js",
"/scripts/common/chosen.jquery.custom.js"
};
}
else
{
return new string[]{
"/scripts/common/commonbundle.js"
};
}
}
}
}
I'm by no means happy with this solution. Please make suggestions if you can improve on this.
I can suggest an alternative from this blog post create your own token.
In summary the author suggests using web essentials to create the bundled file and then creating a razor helper to generate the token, in this case based on the last changed date and time.
public static class StaticFile
{
public static string Version(string rootRelativePath)
{
if (HttpRuntime.Cache[rootRelativePath] == null)
{
var absolutePath = HostingEnvironment.MapPath(rootRelativePath);
var lastChangedDateTime = File.GetLastWriteTime(absolutePath);
if (rootRelativePath.StartsWith("~"))
{
rootRelativePath = rootRelativePath.Substring(1);
}
var versionedUrl = rootRelativePath + "?v=" + lastChangedDateTime.Ticks;
HttpRuntime.Cache.Insert(rootRelativePath, versionedUrl, new CacheDependency(absolutePath));
}
return HttpRuntime.Cache[rootRelativePath] as string;
}
}
Then you can reference the bundled file like so...
#section scripts {
<script src="#StaticFile.Version("~/Scripts/app/myAppBundle.min.js")"></script>}
Then you have control of the token and can do what you want with it.

Sterling database created from Windows Console app won't read inside WP7 app

I have created a Sterling database inside a standard Windows console app, then I have added that database file as a resource inside a WP7 app. I find that the database reading code causing an ArgumentNullException when accessing the LazyValue.Value member.
Here's the database creation code, excluding the model 'Venue'.
public class TestDatabaseInstance : BaseDatabaseInstance
{
public override string Name
{
get
{
return "TestDatabase";
}
}
protected override List<ITableDefinition> RegisterTables()
{
return new List<ITableDefinition>
{
CreateTableDefinition<Venue, int>(x=>x.VenueId)
};
}
}
class Program
{
static void Main(string[] args)
{
//CreateData();
LoadData();
}
private static void CreateData()
{
using (SterlingEngine engine = new SterlingEngine())
{
engine.Activate();
var databaseInstance = engine.SterlingDatabase.RegisterDatabase<TestDatabaseInstance>();
for (int i = 100; i < 1000; i++)
{
var venue = new Venue();
venue.Name = "test";
venue.AddressLine1 = "this is an address";
venue.VenueId = i;
var key = databaseInstance.Save<Venue>(venue);
}
FileStream fs = File.Open("c:\\myvenuedata.dat", FileMode.CreateNew, FileAccess.ReadWrite, FileShare.Write);
using (var binaryWriter = new BinaryWriter(fs))
{
engine.SterlingDatabase.Backup<TestDatabaseInstance>(binaryWriter);
}
databaseInstance = null;
}
}
private static void LoadData()
{
SterlingEngine engine = new SterlingEngine();
var fs = File.Open("c:\\myvenuedata.dat", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite);
engine.Activate();
var databaseInstance = engine.SterlingDatabase.RegisterDatabase<TestDatabaseInstance>();
engine.SterlingDatabase.Restore<TestDatabaseInstance>(new BinaryReader(fs));
engine.Dispose();
engine = new SterlingEngine();
engine.Activate();
// THIS LINE WORKS FINE IN MY CONSOLE APP
databaseInstance.Query<Venue, int>().ForEach(x => Console.WriteLine(x.LazyValue.Value.Name));
}
}
Then if I put the equivalent code inside the WP7 app:
SterlingEngine engine = new SterlingEngine();
StreamResourceInfo sri = Application.GetResourceStream(new Uri("/SterlingDBReader;component/myvenuedata.dat", UriKind.RelativeOrAbsolute));
var fs = sri.Stream;
engine.Activate();
var databaseInstance = engine.SterlingDatabase.RegisterDatabase<TestDatabaseInstance>();
engine.SterlingDatabase.Restore<TestDatabaseInstance>(new BinaryReader(fs));
engine.Dispose();
engine = new SterlingEngine();
engine.Activate();
// **Errors with ArgumentNullException here because x.LazyValue.Value IS NULL.**
databaseInstance.Query<Venue, int>().ForEach(x => Debug.WriteLine(x.LazyValue.Value.Name));
The only differences are
that the parent namespace of the reader WP7 app is different to the console app
It's a WP7 app reading from a console app created data file
It loads the data file from isolated storage as a resource
Any ideas?
thanks
Kris
Currently Sterling stores types using the fully qualified assembly type name. That means the referenced classes should be in the exact same project = preferably a shared Silverlight 3 DLL. If you are just linking the files and recompiling it won't work due to this. The goal is to change this in version 2.0 to improve the type checking but that's the case for now.
As far as I know, the type is an important part of the storage mechanism for Sterling, so you'd need to make sure that the namespaces and type names of the type that are stored in the database to match exactly.
I have no idea if the scenarion your suggesting is a supported one, though the restore approach sounds like it should work. I'd recommend asking on CodePlex.
Given the exception you're getting, it sounds like the type names might not match properly, hence the LazyValue.Value being null.

Categories

Resources