I have created a blog with ASP.NET MVC 4.
The problem is when my url is not localhost I cannot see any pictures inside my Posts, because they get wrong url. The url of the post can be:
1.localhost/post/2015/4/name_of_post or
2.localhost/archive/name_of_post or
3.localhost/name_of_category/name_of_post ,
and inside the post is the picture.
The picture must have this
url:localhost/App_Files/Upload/name_of_image.jpg
but gets instead this
url:localhost/post/2015/4/App_Files/Upload/name_of_image.jpg or
localhost/archive/name_of_post or
localhost/name_of_category/App_Files/Upload/name_of_image.jpg.
If you use the right url you will see the picture in browser.
I use tinymce to add pictures inside the the post when i create it. The picture is saved as file in the App_Files/Upload folder. The post is inserted in a row as html in sql database. I use this routeConfig
routes.MapRoute(
"Post",
"Archive/{year}/{month}/{title}",
new { controller = "Blog", action = "Post" }
);
and the uploadController is this:
using HotNews.ViewModels;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.IO;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace HotNews.Controllers
{
public class UploadController : Controller
{
//
// GET: /Upload/
public ActionResult Index(string id)
{
//var url = HttpRuntime.AppDomainAppVirtualPath;
var url = ConfigurationManager.AppSettings["BlogUrl"];
return View(new UploadViewModel
{
baseUrl = url,
track = id
});
}
[HttpPost]
public ActionResult UploadFile()
{
var file = Request.Files[0];
if (file != null && file.ContentLength > 0)
{
var fileName = Path.GetFileName(file.FileName);
var path = Path.Combine(Server.MapPath("~/App_Files/Upload"), fileName);
file.SaveAs(path);
}
return RedirectToAction("Index");
}
public ActionResult ListFiles()
{
var fileData = new List<ViewDataUploadFileResults>();
DirectoryInfo dir = new DirectoryInfo(Server.MapPath("~/App_Files/Upload"));
if (dir.Exists)
{
string[] extensions = MimeTypes.ImageMimeTypes.Keys.ToArray();
FileInfo[] files = dir.EnumerateFiles()
.Where(f => extensions.Contains(f.Extension.ToLower()))
.ToArray();
if (files.Length > 0)
{
foreach (FileInfo file in files)
{
// var baseurl = ConfigurationManager.AppSettings["BlogUrl"];
var relativePath = VirtualPathUtility.ToAbsolute("~/App_Files/Upload") + "/" + file.Name;
fileData.Add(new ViewDataUploadFileResults()
{
// url = baseurl+relativePath,
url = relativePath,
name = file.Name,
type = MimeTypes.ImageMimeTypes[file.Extension],
size = Convert.ToInt32(file.Length)
});
}
}
}
return Json(fileData, JsonRequestBehavior.AllowGet);
}
}
}
Must I create an image hanndler?? Must I create a new routeconfig?? And what kind??
Related
I am trying to write an action to upload a file and when I try to call GetFileName() method I get this error:
'IFormFile' does not contain a definition for 'GetFileName' and no accessible extension method 'GetFileName' accepting a first argument of type 'IFormFile' could be found (are you missing a using directive or an assembly reference?)
my controller uses the following namespaces:
using Microsoft.Extensions.FileProviders;
using System.IO;
using Microsoft.AspNetCore.Http;
and the action is:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var path = Path.Combine(
Directory.GetCurrentDirectory(), "wwwroot", file.GetFileName());
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return RedirectToAction("Files");
}
To get the filename of the uploaded file using IFormFile we can get it using file.FileName.
Try this:
[HttpPost]
public async Task<IActionResult> UploadFile(IFormFile file)
{
if (file == null || file.Length == 0)
return Content("file not selected");
var path = Path.Combine(
Directory.GetCurrentDirectory(), "wwwroot", file.FileName);
using (var stream = new FileStream(path, FileMode.Create))
{
await file.CopyToAsync(stream);
}
return RedirectToAction("Files");
}
I want to create an extension method of #Html.Action But Showing Error for IActionSelectorDecisionTreeProvider could not load namespace
var actionSelector = GetServiceOrFail<IActionSelectorDecisionTreeProvider>(currentHttpContext);
Error Screen Short
I have installed all dependencies of dot.net 5 like
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.AspNetCore.Routing;
But No Luck.
Working Code In Dot Net 5
public static class HtmlHelperViewExtensions
{
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, object parameters = null)
{
var controller = (string)helper.ViewContext.RouteData.Values["controller"];
return RenderAction(helper, action, controller, parameters);
}
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, object parameters = null)
{
var area = (string)helper.ViewContext.RouteData.Values["area"];
return RenderAction(helper, action, controller, area, parameters);
}
public static IHtmlContent RenderAction(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
if (action == null)
throw new ArgumentNullException("action");
if (controller == null)
throw new ArgumentNullException("controller");
//if (area == null)
// throw new ArgumentNullException("area");
var task = RenderActionAsync(helper, action, controller, area, parameters);
return task.Result;
}
private static async Task<IHtmlContent> RenderActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
// fetching required services for invocation
var currentHttpContext = helper.ViewContext?.HttpContext;
var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext);
// creating new action invocation context
var routeData = new RouteData();
var routeParams = new RouteValueDictionary(parameters ?? new { });
var routeValues = new RouteValueDictionary(new { area = area, controller = controller, action = action });
var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);
newHttpContext.Response.Body = new MemoryStream();
foreach (var router in helper.ViewContext.RouteData.Routers)
routeData.PushState(router, null, null);
routeData.PushState(null, routeValues, null);
routeData.PushState(null, routeParams, null);
var actionDescriptor = actionSelector.ActionDescriptors.Items.Where(i => i.RouteValues["controller"] == controller && i.RouteValues["action"] == action).First();
var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
// invoke action and retreive the response body
var invoker = actionInvokerFactory.CreateInvoker(actionContext);
string content = null;
await invoker.InvokeAsync().ContinueWith(task => {
if (task.IsFaulted)
{
content = task.Exception.Message;
}
else if (task.IsCompleted)
{
newHttpContext.Response.Body.Position = 0;
using (var reader = new StreamReader(newHttpContext.Response.Body))
content = reader.ReadToEnd();
}
});
return new HtmlString(content);
}
private static TService GetServiceOrFail<TService>(HttpContext httpContext)
{
if (httpContext == null)
throw new ArgumentNullException(nameof(httpContext));
var service = httpContext.RequestServices.GetService(typeof(TService));
if (service == null)
throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");
return (TService)service;
}
}
Is there any way to implement #Html.Action method in ASP.NET Core (like it was in ASP.NET MVC)? I know about the ViewComponent feature of ASP.NET Core. But there is a scenario when we have to use Action method.
This is the way to implement this as a HtmlHelper extension.
You can use it as follows:
The last parameter is an anonymous type.
#Html.Action("Action");
#Html.Action("Action", new { string a = "a", int i = 5 }
#Html.Action("Action", "Controller");
#Html.Action("Action", "Controller", new (string a = "a", int i = 5 }
using System;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Html;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc.Infrastructure;
using Microsoft.AspNetCore.Routing;
namespace Microsoft.AspNetCore.Mvc.Rendering
{
public static class HtmlHelperViewExtensions
{
public static IHtmlContent Action(this IHtmlHelper helper, string action, object parameters = null)
{
var controller = (string)helper.ViewContext.RouteData.Values["controller"];
return Action(helper, action, controller, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, object parameters = null)
{
var area = (string)helper.ViewContext.RouteData.Values["area"];
return Action(helper, action, controller, area, parameters);
}
public static IHtmlContent Action(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
if (action == null)
throw new ArgumentNullException(nameof(controller));
if (controller == null)
throw new ArgumentNullException(nameof(action));
var task = ActionAsync(helper, action, controller, area, parameters);
return task.Result;
}
private static async Task<IHtmlContent> ActionAsync(this IHtmlHelper helper, string action, string controller, string area, object parameters = null)
{
// fetching required services for invocation
var currentHttpContext = helper.ViewContext.HttpContext;
var httpContextFactory = GetServiceOrFail<IHttpContextFactory>(currentHttpContext);
var actionInvokerFactory = GetServiceOrFail<IActionInvokerFactory>(currentHttpContext);
var actionSelector = GetServiceOrFail<IActionDescriptorCollectionProvider>(currentHttpContext);
// creating new action invocation context
var routeData = new RouteData();
var routeParams = new RouteValueDictionary(parameters ?? new { });
var routeValues = new RouteValueDictionary(new { area, controller, action });
var newHttpContext = httpContextFactory.Create(currentHttpContext.Features);
newHttpContext.Response.Body = new MemoryStream();
foreach (var router in helper.ViewContext.RouteData.Routers)
routeData.PushState(router, null, null);
routeData.PushState(null, routeValues, null);
routeData.PushState(null, routeParams, null);
var actionDescriptor = actionSelector.ActionDescriptors.Items.First(i => i.RouteValues["Controller"] == controller && i.RouteValues["Action"] == action);
var actionContext = new ActionContext(newHttpContext, routeData, actionDescriptor);
// invoke action and retreive the response body
var invoker = actionInvokerFactory.CreateInvoker(actionContext);
string content = null;
await invoker.InvokeAsync().ContinueWith(task =>
{
if (task.IsFaulted)
{
content = task.Exception.Message;
}
else if (task.IsCompleted)
{
newHttpContext.Response.Body.Position = 0;
using (var reader = new StreamReader(newHttpContext.Response.Body))
content = reader.ReadToEnd();
}
});
return new HtmlString(content);
}
private static TService GetServiceOrFail<TService>(HttpContext httpContext)
{
if (httpContext == null)
throw new ArgumentNullException(nameof(httpContext));
var service = httpContext.RequestServices.GetService(typeof(TService));
if (service == null)
throw new InvalidOperationException($"Could not locate service: {nameof(TService)}");
return (TService)service;
}
}
}
While it does work and renders the content correctly, when you try to access the IHttpContextAccessor.HttpContext property afterwards it is empty for some reason. I.e. you are opening another controller method which has a reference to IHttpContextAccessor via dependency injection and try to access the HttpContext property after you have renderend an Html.Action-element inside a partial view for example. If I remove the Html.Action-Element, HttpContext is filled correctly. I assume it somehow destroys the context.
we have a textarea using CKEditor 4.4 on our admin website where users can edit content. They would like to be able to add images from their computer and have them uploaded automatically to the server for hosting.
I've seen a number of image upload scripts for CKEditor, but they all come with a PHP back-end. Does one exist for ASP.NET MVC 4?
I've seen this post and this one which show server-side controls for WebForms, but haven't been able to find an MVC version that we could drop in, or modify to our tastes.
Is my only option to use one of the existing PHP plugins and rewrite the endpoints as ASP.NET MVC?
Thanks.
Based on Alfonso's WebForms code mentioned in the accepted answer, I ended up using a script similar to this in MVC:
using System.Web;
using System.Web.Mvc;
namespace WebApplication1.Controllers
{
public class CKEditorController : Controller
{
const string basePath = #"D:\CKFinder\ckfinder\userfiles\";
const string baseUrl = #"/ckfinder/userfiles/";
const string scriptTag = "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction({0}, '{1}', '{2}')</script>";
public ActionResult Index()
{
var funcNum = 0;
int.TryParse(Request["CKEditorFuncNum"], out funcNum);
if (Request.Files == null || Request.Files.Count < 1)
return BuildReturnScript(funcNum, null, "No file has been sent");
if (!System.IO.Directory.Exists(basePath))
return BuildReturnScript(funcNum, null, "basePath folder doesn't exist");
var receivedFile = Request.Files[0];
var fileName = receivedFile.FileName;
if (string.IsNullOrEmpty(fileName))
{
return BuildReturnScript(funcNum, null, "File name is empty");
}
var sFileName = System.IO.Path.GetFileName(fileName);
var nameWithFullPath = System.IO.Path.Combine(basePath, sFileName);
//Note: you may want to consider using your own naming convention for files, as this is vulnerable to overwrites
//e.g. at the moment if two users uploaded a file called image1.jpg, one would clash with the other.
//In the past, I've used Guid.NewGuid() combined with the file extension to ensure uniqueness.
receivedFile.SaveAs(nameWithFullPath);
var url = baseUrl + sFileName;
return BuildReturnScript(funcNum, url, null);
}
private ContentResult BuildReturnScript(int functionNumber, string url, string errorMessage)
{
return Content(
string.Format(scriptTag, functionNumber, HttpUtility.JavaScriptStringEncode(url ?? ""), HttpUtility.JavaScriptStringEncode(errorMessage ?? "")),
"text/html"
);
}
}
}
These aren't exactly MVC samples, but you can find a sample in VB.Net and C# to handle uploads from CKEditor: https://github.com/AlfonsoML/CKEditorUploader
Pick the code that you want and adjust it to your CMS.
The plugin sends the image asynchronously to the server. As long as you have an ASP.NET MVC/Web Api end point to accept the image and save it to the relavant place/update relevant tables, You should be good. Make sure you return data which your plugin is expecting.
for example, from the demo page you provided, the PHP server page is returning the following string on successful upload of the image
<script type="text/javascript">
window.parent.CKEDITOR.tools.callFunction("92", "\/userfiles\/images\/myImgy.jpg", "");
</script>
In your Web api endpoint, You can use HttpContext.Current.Request.Files collection to look for the posted files.
Try this
Html and JavaScript
<script src="~/Vendors/ckeditor/ckeditor.js"></script>
<script src="~/Vendors/ckeditor/adapters/jquery.js"></script>
<div class="jumbotron">
<textarea name="editor1"></textarea>
<script>
CKEDITOR.replace('editor1', {
uiColor: '#9AB8F3',
filebrowserUploadUrl: '/CkEditorUpload/'
});
</script>
</div>
Controller
using System;
using System.IO;
using System.Web;
using System.Web.Mvc;
namespace ImageUploadCkEditor.Controllers
{
public class CkEditorUploadController : Controller
{
const string filesavepath = "~/Content/Uploads/Ckeditor";
const string baseUrl = #"/Content/Uploads/Ckeditor/";
const string scriptTag = "<script type='text/javascript'>window.parent.CKEDITOR.tools.callFunction({0}, '{1}', '{2}')</script>";
public ActionResult Index()
{
var funcNum = 0;
int.TryParse(Request["CKEditorFuncNum"], out funcNum);
if (Request.Files == null || Request.Files.Count < 1)
return BuildReturnScript(funcNum, null, "No file has been sent");
string fileName = string.Empty;
SaveAttatchedFile(filesavepath, Request, ref fileName);
var url = baseUrl + fileName;
return BuildReturnScript(funcNum, url, null);
}
private ContentResult BuildReturnScript(int functionNumber, string url, string errorMessage)
{
return Content(
string.Format(scriptTag, functionNumber, HttpUtility.JavaScriptStringEncode(url ?? ""), HttpUtility.JavaScriptStringEncode(errorMessage ?? "")),
"text/html"
);
}
private void SaveAttatchedFile(string filepath, HttpRequestBase Request, ref string fileName)
{
for (int i = 0; i < Request.Files.Count; i++)
{
var file = Request.Files[i];
if (file != null && file.ContentLength > 0)
{
fileName = Path.GetFileName(file.FileName);
string targetPath = Server.MapPath(filepath);
if (!Directory.Exists(targetPath))
{
Directory.CreateDirectory(targetPath);
}
fileName = Guid.NewGuid() + fileName;
string fileSavePath = Path.Combine(targetPath, fileName);
file.SaveAs(fileSavePath);
}
}
}
}
}
I need my app to send a confirmation email to a user. I have used the following method to render the view as a string:
public string RenderViewToString<T>(string viewPath, T model)
{
using (var writer = new StringWriter())
{
var view = new WebFormView(viewPath);
var vdd = new ViewDataDictionary<T>(model);
var viewCxt = new ViewContext(ControllerContext, view, vdd, new TempDataDictionary(), writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
which I got from here. It works great, however my images aren't being included. I'm using:
<img src="<%:Url.Content("~/Resource/confirmation-email/imageName.png") %>"
which is giving me
http://resource/confirmation-email/imageName.png
This works fine when viewing the page on the site, however the image links don't work in the email.
I need it to give me me:
http://domain.com/application/resource/confirmation-email/imageName.png
I've also tried using:
VirtualPathUtility.ToAbsolute()
This is what I used on a site recently:
public static string ResolveServerUrl(string serverUrl, bool forceHttps = false, bool getVirtualPath = true)
{
if (getVirtualPath)
serverUrl = VirtualPathUtility.ToAbsolute(serverUrl);
if (serverUrl.IndexOf("://") > -1)
return serverUrl;
string newUrl = serverUrl;
Uri originalUri = System.Web.HttpContext.Current.Request.Url;
newUrl = (forceHttps ? "https" : originalUri.Scheme) + "://" + originalUri.Authority + newUrl;
return newUrl;
}
I could then use it to generate Absolute urls by doing Core.ResolveServerUrl("~/Resource/confirmation-email/imageName.png"); (assuming you wrap the static function in a class named Core)
HTH
There isn't a way to do this. You can add the following extension method.
using System.Web.Mvc;
public static class UrlHelperExtensions
{
public static string ToAbsoluteUrl(this UrlHelper helper, string relativeUrl) {
if (Request.IsSecureConnection)
return string.Format("https://{0}{1}", Request.Url.Host, Page.ResolveUrl(relativeUrl));
else
return string.Format("http://{0}{1}", Request.Url.Host, Page.ResolveUrl(relativeUrl));
}
}
Which you can then call like so
<img src="<%:Url.ToAbsoluteUrl("~/Resource/confirmation-email/imageName.png") %>" ...