I'm on a solution with multiple projects, and I'm having issues accessing a project outside the one I'm currently on. Here's a very simplified structure of my codebase:
-ProjectA
-Pages
Index.razor
-ProjectB
- Images
-image.jpg
I'm on ProjectA using Blazor Server and I need to read an image outside my directory in ProjectB. I created a static file using the following code:
app.UseStaticFiles(new StaticFileOptions()
{
FileProvider = new PhysicalFileProvider(
Path.Combine(Directory.GetCurrentDirectory(), #"./../ProjectB/Images/")),
RequestPath = new PathString("/MyImages")
});
The thing is, when I use "/MyImages" inside of an html img tag followed with the filename, it works fine and I can view any image in ProjectB. However, this does not work when I'm using System.IO code such as:
#code{
File.ReadAllBytes("/MyImages/image.jpg");
}
Can someone let me know what I'm doing wrong?
EDIT: I should have been a bit more clearer on the purpose of my project.
I have three classes that I'm dealing with, Product, CreateProduct and EditProduct. Those classes have the same properties like Name, Price, etc. The only difference is that Product has the string ImagePath while CreateProduct and EditProduct have IFormFile Image.
When I want to create a new Product, I read the string values like name, price, etc, then for the image, I use InputFile which gives me a IBrowserFile, do some basic conversion process to an IFormFile then POST it as a CreateProduct. In the backend, the API will store the IFormFile in the file system (aka ProjectB) then create a Product record in the DB which has the ImagePath.
Here's the snippet of code for this process:
[HttpPost]
public async Task<ActionResult<Product>> PostProduct([FromForm] CreateProduct model)
{
try
{
if (!ModelState.IsValid)
return BadRequest();
// This function helpers.UploadFiles will just write the file to the file system, then return the url
// For example, it'll return "/product_1/image.jpg"
var ProductImagePath = await _helpers.UploadFiles(model.Image);
var product = new Product
{
//news
Name = model.Name,
Price = model.Price,
ImagePath = ProductImagePath,
CreatedDate = DateTime.Now
};
_context.Product.Add(Product);
await _context.SaveChangesAsync();
And this is how it looks on the frontend:
// Converting my IBrwserFile(imageFile) to bytes so that it can be posted as IForm (Probably could do it better but that's an issue for another day)
using Stream filestream = imageFile.OpenReadStream(1024 * 1024 * 15);
using MemoryStream ms = new();
await filestream.CopyToAsync(ms);
byte[] productImage = ms.ToArray();
var formContent = new MultipartFormDataContent
{
{new StringContent("SomeProductName"),"Name"},
{new StringContent("SomeProductPrice"),"Price" },
{
new ByteArrayContent(productImage), "Image", fileName
},
};
await client.PostAsync("Product/", formContent);
My PUT request follows the same structure. The tricky part is the EditProduct scenario. I first have to do a GET request and receive the Product instance which only has the image path. There are two scenarios that editing my product entails:
Scenario 1:
1- Make a GET request for the `Product` class which has the image path
2- View all information including the image on the browser using <img src="ImagePath">
3- Decide to use a different image, which then I just call InputFile
4- Process InputFile and convert it to IFormFile
5- Make a PUT request and post the updated information to `EditForm` class
This works out fine. However in Scenario 2:
1- Steps 1 & 2 as before
2- Decide not to change the image, but change other stuff like the Name and price
3- ????
Now I need to make a PUT request but since I didn't use InputFile, I don't have an IFormFile that I can send to the PUT request for EditForm class.
In order for the PUT process to work properly, I need to read the image I got from the ImagePath using File.ReadAllBytes then read it to memory and convert it to an IFormFile.
As I mentioned at the start I can view the image using <img src="/MyImages/#imagePath />, with /MyImages being the static file path I created on Startup.cs. That doesn't work when I apply it to File.ReadAllBytes or any other IO functions.
It seems to me that your two projects are compiled under different folder systems, but are hosted under the same domain (say, "localhost/A" and "localhost/B"). From your browser's point of view, there's no problem.
But if you are trying to access files in your physical system, there's a problem because they have different root paths, and aren't actually sharing any folders. (which is as it should be, since you don't want 2 apps struggling for control over the file system)
Why do you want direct access to the image files? Will you manipulate them from your other project somehow?
Related
I am trying to 'upload' a video file in to my application and pass it to the database. I am able to handle pictures quite nicely however as soon as I try larger / video files I am getting a Status: 400 error code when a Submit is made.
I put a break point on the OnPostAsync method in code behind but it doesn't get hit.
I just have a standard Input --> Type: File:
and from seeing a few mentions online I have manually added a web.config file to the project and added the following:
However I still get the 400 error if I attempt to upload a large file. I notice in the actual call it should allow the mime type:
As per other advice I have also tried adding the following to the
but get the following:
And also on the model that is being populated with the large file data:
Still the same error.
I would be interested to hear peoples suggestions on what could be causing my error.
I have managed to get this working eventually by adding the following to the Startup.cs file:
Code:
services.Configure<FormOptions>(x =>
{
x.ValueLengthLimit = int.MaxValue;
x.MultipartBodyLengthLimit = int.MaxValue;
x.MultipartHeadersLengthLimit = int.MaxValue;
});
services.Configure<KestrelServerOptions>(options =>
{
options.Limits.MaxRequestBodySize = int.MaxValue;
});
Now I am able to load large files as expected.
In ASP.NET Core MVC, You can put the below code (Attribute) before your method (Action) but in Razor pages, We can not put it before method (Handler)! and the compiler will notify warning and the code does not work!
[RequestFormLimits(MultipartBodyLengthLimit = 104857600)]
So, In ASP.NET Core Razor Pages, We should put the below code (Attribute) before the class (PageModel) not before the method (Handler)!
[RequestFormLimits(MultipartBodyLengthLimit = 104857600)]
public class UploadFile : PageModel
...
Add this line just before the method where you try to upload a large file.
[RequestFormLimits(MultipartBodyLengthLimit = 104857600)]
I currently have my solution set up to produce Swagger documentation for each end point. However I have several end points that are only available for admins. Down below you will be able to see an example.
A regular user can create models, however only an admin can pull every single model in the database.
The challenge is to generate 2 sets of swagger documentation? One for regular users to see, and another piece of documentation for Admin users to see. I know that if I add [ApiExplorerSettings(IgnoreApi = true)] to my end point it will not appear in the documentation generated however this would mean that my admin users wont be able to see that vital piece of documentation as well. Any recommendation on how to dynamically generate two sets of documents depending on the user will help.
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<PackageResponse>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[HttpPost("/v1/packages")]
[Authorize()]
public async Task<IActionResult> CreateModel([FromBody]Request request)
{
...
}
The method below is for admins only:
[SwaggerResponse((int)System.Net.HttpStatusCode.OK, Type = typeof(RestOkResponse<PackageResponse>))]
[SwaggerResponse((int)System.Net.HttpStatusCode.InternalServerError, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.BadRequest, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.Forbidden, Type = typeof(RestErrorResponse))]
[SwaggerResponse((int)System.Net.HttpStatusCode.NotFound)]
[ApiExplorerSettings(IgnoreApi = true)]
[HttpPost("/v1/packages")]
[Authorize()]
public async Task<IActionResult> GetAllModelsFromDatabase([FromBody]Request request)
{
...
}
A dynamic process was found in this answer.
Dynamically Ignore WebAPI method on controller for api explorer documentation
It is possible to separate swagger documents however there is no built in method to do this. One would have to remove the un wanted nodes from the one documentation file:
https://github.com/swagger-api/swagger-editor/issues/233
This works fine in current editor if you host the editor yourself so
parameters_common.yaml path can get resolved as an HTTP path.
Currently there is no way to jump between files or create a new one.
If you are doing a big project with Swagger, I recommend hosting the
editor yourself. When editor and the API you are building are on the
same origin, XHR call don't have to be cross-origin which help editor
to show more details about calls in "try-operation" and your API
doesn't have to have cross origin headers.
Example on how to split swagger file into smaller nodes.
http://azimi.me/2015/07/16/split-swagger-into-smaller-files.html
This might be a silly question but I got confuse on the use of FileStream.
At first I thought FileStream is just an System.IO implementation for reading and saving files but there is also FILESTREAM for MSSQL or Database Storage.
https://msdn.microsoft.com/en-us/library/bb933993%28v=sql.100%29.aspx
My problem here is I would like to implement FILESTREAM on my application since I am just going to store profilepictures but what I dont get is the implementation part.
There are no examples or mention on how to use it in Entity Framework like what Filetype to be use or do I need to install a nuget. If I search for implementation of FileStream what I get is System.IO which does not have any reference for saving in database since it is use for file system
I am so confused. Please help.
It looks like that SQL Server FileStream is an over engineering solution for just keeping users avatars on oyour server. You can easily store the avatars on your server as files in some particular folder with having a corresponding record in your avatars reference table that keep the file path or store the avatar as a VARBINARY field in an avatars table.
Storing avatars as files seems to me a more straight forward solution because later on you can add an url of the file in some <img/> in purpose to display the avatar.
As for the rest I would suggest the following:
1) In your MVC view please reffer a model like that
public class AvatarModel
{
public int UserId { get; set; }
public HttpPostedFileBase AvatarFile { get; set; }
}
2) Start your Avatar View with the following statement
using (
Html.BeginForm("Avatar", "UserManagement", FormMethod.Post, FormMethod.Post, new { enctype = "multipart/form-data" } )
)
3) In the View define the inputs and submit button
<input type="hidden" id="UserId" value="#Model.UserId" />
<input type="file" id="AvatarFile" />
<button type="submit"/>
4) In your UserManagement controller implement the following method
[HttpPost]
[ActionName("Avatar")]
public ActionResult AvatarPost(AvatarModel model)
{
...
}
5) in the body of the method you will acess your file over the model argument
model.AvatarFile. The object model.AvatarFile gives you two possibilities either save as a file https://msdn.microsoft.com/en-us/library/system.web.httppostedfilebase.saveas(v=vs.110).aspx or you can read the file as a stream https://msdn.microsoft.com/en-us/library/system.web.httppostedfilebase.inputstream(v=vs.110).aspx convert it to a byte[] and then assign the byte array to a particular model of your entityt framework model.
Please keep in mind that by uploading the file you should keep the POST message <=64 Kbytes. In other case you will have customize your application config and the config of IIS on your server.
But 64 Kbytes should be enought for a avatat isn't it?
We are developing an e-commerce system that multiple affiliate partners will use. We would like to tailor the portal for each partner and be able to accommodate slight variations in content from page to page. Our current technique has been to create a copy of a .cshtml view for each partner and make the customization to each view. Our designer is groaning because may of these views only have slight variations in wording. We only plan to have 10 or so partners (it cannot expand beyond that because of the size of our industry) so a full blown CMS system is overkill.
I would like to use resx files manage content strings for each partner the way one would use them to manage content strings for different languages. The end result would be the ability to do something like this in a view.
Please contact customer service at #Properties.Resources.PartnerCustomerServiceEmail
at not have to worry about which resource file is used to resolve the string PartnerCustomerServiceEmail.
Thank you in advance for your help
First idea that comes to my mind is to save resource file's name in question into viewdata (or Session) and use a helper to get the value.
Say you have two partners: Foo Logistics and Bar Solutions. Have a resource file for each of them: PartnerFoo.resx and PartnerBar.resx.
In your controller, store the resource file you want to use into ViewData as in:
public ActionResult About()
{
...
ViewData["Resource"] = "MyMVCAppNamespace.Resources.PartnerFoo";
return View();
}
Include the namespace into the string too.
Then code in the helper to retrieve the resource with viewdata.
Helpers/Helper.cs:
namespace MyMVCAppNamespace.MvcHtmlHelpers
{
public static class HtmlHelpersExtensions
{
public static ResourceManager PartnerResource(this HtmlHelper helper)
{
// Get resource filename from viewdata
string res = helper.ViewContext.ViewData["Resource"].ToString();
// Load the resource from assembly
ResourceManager resourceManager = new ResourceManager(res, Assembly.GetExecutingAssembly());
return resourceManager;
}
}
}
Now in the view, we are gonna use this helper to retrieve the string we want to write:
About.cshtml:
#Html.PartnerResource().GetString("PartnerName")
#Html.PartnerResource().GetString("PartnerCustomerServiceEmail")
Which gets rendered as:
Foo Logistics service#foologistics.com
or with PartnerBar
Bar Solutions service#barsolutions.com
We determine the resource file to use before the view is loaded. Then in view it gets dynamically rendered according to what resource is stored in to the viewdata. You can even store the resource filename into web.config and load the string in helper from there if you want.
What's even more cool is that if you have localized resx file, say PartnerFoo.fi.resx and then use different culture (fi-FI in this case), the resourcemanager automatically looks up the localized version.
You can even do simple branding by storing image URLs and whatnot in the resource file.
It's simple really, but I hope it gets you started.
I have a web MVC application that I would like to add feature that gives users the ability to download large files from my server. The users have a combination of Mac and Windows PC. I was thinking along the lines of javasripts or silverlight.
Can someone advice me on how to implement this feature?
Do you have any code examples?
Use the File method of Controller class.
So Create a Controller called FilesController and have an action method called DownLoad
public class FilesController : Controller
{
public ActionResult Download(string fileId)
{
var fullFilePath=FileService.GetFullPath(fileId); // get the path to file
return File(fullFilePath,"application/pdf","yourDownLoadName.pdf");
}
}
This Will return a PDF file from the specified path(fullFilePath) with the MimeType/ContentType as PDF and "yourDownLoadName.pdf" as the Downloadable file name
Users can access this like http://yourdomainname.com/Files/Download?fileId=somefileId
This method has got a bunch of overloads using file path, byte array ,stream etc..
Create a controller action with a FileStreamResult return type.