Not allowed to load local resource - angularjs

Currently i am running one MVC 5 project with web api where i am using angular js ajax call to fire web api method and get image path but when i am setting that path in img tag so image is not displaying and in chrome console message displays that not able to load local resource.
Below is the web api method code which i am calling through ajax
[Route("Files")]
public List<FilesInfo> GetImages()
{
rootPath = HostingEnvironment.MapPath("~/Content/Images/");
List<FilesInfo> files = new List<FilesInfo>();
foreach (var item in Directory.GetFiles(rootPath))
{
FileInfo f = new FileInfo(item);
files.Add(new FilesInfo(){FileName=f.Name, FilePath=rootPath+f.Name});
}
return files;
}
Please let me know if you have any queries.

It seems HostingEnvironment.MapPath returns the physical path on the server.
When you are adding this path to the src attribute of your img, the browser complains it cannot local local resource.
You'd need to adjust the path to the image to be relative to your site: /img/img1.jpg
Try:
[Route("Files")]
public List<FilesInfo> GetImages()
{
rootPath = "/Content/Images/";
List<FilesInfo> files = new List<FilesInfo>();
foreach (var item in Directory.GetFiles(HostingEnvironment.MapPath("~"+ rootPath);))
{
FileInfo f = new FileInfo(item);
files.Add(new FilesInfo() { FileName = f.Name, FilePath = rootPath + f.Name });
}
return files;
}
This way System.IO.FileInfo should have the local path, and your FilesInfo should have the relative url path, which you can use with angular to create your: <img ng-src="{{vm.file.filePath}}" alt="{{vm.file.fileName}}" />

Related

axios.post correct path param

Super newbie to ASP.NET Core with React,
and try to build "upload file" functions.
From my react component,
I have input as follows, and trigger UploadFile function
<input type="file" onChange={(event) => UploadFile(event)} />
and in UploadFile function, it looks like this
function UploadFile(e) {
var file = e.target.files[0];
var formData = new FormData();
formData.append("file", file);
axios.post("/FileUpload/SingleFile", formData);
}
and the controller for SingleFile looks like this
public IActionResult SingleFile(IFormFile file)
{
using (var fileStream = new FileStream(Path.Combine(_dir, "file.png"), FileMode.Create, FileAccess.Write))
{
file.CopyTo(fileStream);
}
return RedirectToAction("Index");
}
But having a bit tricky time to figure out what is the right url path to pass on to the
axios.post(???, formData).
Is the path should be
"/'controller name'/'IActionResult method name'"??
With the current path, I get an 404 error which means wrong path.
Help!
The path I had was correct,
but the problem was that I was using ASP.NET Core 3.0
so I need to generate the antiforgery token.
The answer is listed here
Enable Antiforgery Token with ASP.NET Core and JQuery

How to S3 + Cloudfrond + 2 SPA react apps in subfolders?

I want to serve 2 different react apps in subfolders using S3 + Cloudfront
myapp.com/app-one/ <-- here is index.html
myapp.com/app-two/ <-- here is another index.html
I know how to configure create-react-app and react router to handle this.
https://create-react-app.dev/docs/deployment/#building-for-relative-paths
The problem is with configuring S3 + Cloudfront to handle redirects.
When you enter url in browser:
myapp.com/app-one/some-route - it should redirect to index.html of app-one
myapp.com/app-two/some-route - it should redirect to index.html of app-two
Unfortunately S3 lets you define only one fallback file.html (404) for Static website hosting
I also don't want to use hashrouter: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/api/HashRouter.md
I was struggling with the same problem for an angular application, and found some solutions, but the one that I found most useful is by using a Lambda#Edge function, this allows you to keep your static files inside the S3 bucket without opening to static hosting.
The only Lambda#Edge configuration that worked for me is the one from this answer. Here is the code:
var path = require('path');
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// Extract the URI from the request
var olduri = request.uri;
// Match any '/' that occurs at the end of a URI. Replace it with a default index
var newuri = olduri.replace(/second-app.*/, 'second-app/index.html');
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
What it basically does is to match the uri of your subfolder and redirect all request to the correct index.html file. If you have multiple sub folders you could simply add some conditions:
var path = require('path');
exports.handler = (event, context, callback) => {
// Extract the request from the CloudFront event that is sent to Lambda#Edge
var request = event.Records[0].cf.request;
const parsedPath = path.parse(request.uri);
// If there is no extension present, attempt to rewrite url
if (parsedPath.ext === '') {
// Extract the URI from the request
var olduri = request.uri;
var newuri = olduri
// Match any '/' that occurs at the end of a URI. Replace it with a default index
if(olduri.match(/first-sub-app.*/)){
newuri = olduri.replace(/first-sub-app.*/, 'first-sub-app/index.html');
} else if(olduri.match(/second-sub-app.*/)){
newuri = olduri.replace(/second-sub-app.*/, 'second-sub-app/index.html');
}
// Replace the received URI with the URI that includes the index page
request.uri = newuri;
}
// If an extension was not present, we are trying to load static access, so allow the request to proceed
// Return to CloudFront
return callback(null, request);
};
Check the original answer for further instructions on the setup and more details on how it works but it basically ignores any request with extensions and only redirects if it matches a specific subdirectory.

Is there a way to dump a thousand images somewhere and extract them using REST Api?

Here is the thing:-
I have over a thousand images saved locally in my mac. I have a landing page that mocks an ecommerce deal site. It would be tedious to have to manually type in the src url in the img tag for a thousand pictures. Thus, i thought i could somehow have this images dumped in a cloud storage or something and use REST api get method to extract these images in a response.data. Then assign it to a $scope variable and use ng-repeat to bind the images in my landing page view. Is this possible? If not, what are the alternatives? SQL database?
Appreciate your help. P.S. I am totally a beginner at web development.
Install node.js. It's Javascript for a server which should make it pretty easy since you already know Javascript.
On a Mac, you can install node like this:
brew install node
Use this node.js code (credit to codepedia.com, tweaked a little by me):
//include http, fs and url module
var http = require('http'),
fs = require('fs'),
path = require('path'),
url = require('url');
imageDir = './images/';
//create http server listening on port 3333
http.createServer(function (req, res) {
//use the url to parse the requested url and get the image name
var query = url.parse(req.url,true).query;
pic = query.image;
if (typeof pic === 'undefined') {
getImages(imageDir, function (err, files) {
var imageList = JSON.stringify(files);
res.writeHead(200, {'Content-type':'application/json'});
res.end(imageList);
});
} else {
//read the image using fs and send the image content back in the response
fs.readFile(imageDir + pic, function (err, content) {
if (err) {
res.writeHead(400, {'Content-type':'text/html'})
console.log(err);
res.end("No such image");
} else {
//specify the content type in the response will be an image
res.writeHead(200,{'Content-type':'image/jpg'});
res.end(content, "binary");
}
});
}
}).listen(3333);
console.log("Server running at http://localhost:3333/");
//get the list of jpg files in the image dir
function getImages(imageDir, callback) {
var fileType = '.jpg',
files = [], i;
fs.readdir(imageDir, function (err, list) {
for(i=0; i<list.length; i++) {
if(path.extname(list[i]) === fileType) {
files.push(list[i]); //store the file name into the array files
}
}
callback(err, files);
});
}
Run this from the command line to start you new image server (assuming you named the file "server.js"):
node server.js
You should see this text appear on the command line:
Server running at http://localhost:3333/
You can quickly test it by going to this address in your browser and you should see a JSON object showing you an array of all the filenames in the "./images" directory. By the way, this program assumes you're putting the images folder in the same directory as "server.js". You can put the images directory anywhere and just change the path of the variable "imageDir".
Now you can load the list of files from Angular using this code in your controller:
$http.get("http://localhost:3333", function(data) {
$scope.images = data;
});
In your view, you can now use an ng-repeat like this to display all the images:
<div ng-repeat="image in images" style="padding: 8px">
<img src="http://localhost:3333/image={{ image }}">
</div>
Note: this will work if you run it locally on your Mac or if you upload all the images to a server on which you can use Node.js.

Is there an alternative for File() constructor for Safari and IE?

I am using the File() constructor for creating file object for uploading a blob file to the server. The following code works fine for Chrome, but fails for Safari and Internet Explorer.
image_url = new File([blob],file_name,{type: mimeString});
The code is breaking at this line and getting this error in console "FileConstructor is not a constructor" (evaluating 'new File([blob],file_name,{type: mimeString})')
Using the FileReader API is an alternative to this but I am not able to fix this issue.
I Suggest to use the blob api, I've found the same problem and I solved like that:
var html = <svg>whatever on svg </svg>
var fileName = "myfile.svg";
var blob = new Blob([html], {type: 'image/svg'});
blob.lastModifiedDate = new Date();
// var blobAttrs = {type: "image/svg"};
// var file = new File([html], fileName, blobAttrs);
var formData = new FormData();
formData.append("file",blob,fileName);
It is not a "file", but you can use it like it was.
According to web "Can I use" Safari does not support the new File() constructor. See this link http://caniuse.com/#feat=fileapi
So I think you have to either use FileReader or maybe use some of the polyfills listed here https://github.com/Modernizr/Modernizr/wiki/HTML5-Cross-Browser-Polyfills
Especially this one could be useful for you https://github.com/mailru/FileAPI (I did not use it myself)
Also have a look at this SO answer What to use instead of FileReader for Safari?
If you can use ES6 classes:
class CustomFile extends Blob
{
constructor(blobParts, filename, options) {
super(blobParts, options);
this.name = filename || "";
if(options) {
this.lastModified = options.lastModified;
}
}
lastModified = 0;
name = "";
}
const blob = new Blob();
const fileObject = new CustomFile([blob],"myfile");
console.log(fileObject);
There is a File ponyfill on npm which works with modern module imports. This makes usage simple but you do need to import it in every module where you use new File().
import File from '#tanker/file-ponyfill';
const myFile = new File(['somefiledata'], 'example.txt', { type: 'text/plain'});

Download a file in Angular Environment

I need to download a file from the server. The file is stored in the database. I have a cs controller that serves a file back to UI. The server GET call looks like this:
http://server/api/controllername/fileid/data
It does work when I run that link in the Browser - the file comes down and goes into the download area (Chrome). But when I send the same command from my Angualar code I dont see any file. The console reports that my request was successful (code 200), but I just dont see anything. Please let me know what code fragments to post to make it easier to help.
Thanks
Create a link to the resource, and don't handle it with ajax.
If you make the link open in a new tab, the tab will automatically close after it realises it was just opened to download a file in most modern browsers.
Try this code:
var a = document.createElement('a');
a.href = "http://server/api/controllername/fileid/data";
a.click();
You can compose the address concatenating variables and text.
The file probably downloads correctly as a byte[] to the calling it but that would be useless to the user - as was my problem.
In my case I needed to download a file with a complex set of parameters. This example JavaScript uses a post request and creates a form (and posts it) with any JavaScript object that you give it. This code may help if you simplified it:
private open(verb, url, data, target)
{
var form = document.createElement("form");
form.action = url;
form.method = verb;
form.target = target || "_self";
if (data) {
this.createFormParameters(form, "", data);
}
form.style.display = 'none';
document.body.appendChild(form);
form.submit();
}
private createFormParameters(form, key, value) {
// recursive algorithm to add parameters (including objects and arrays of objects) to a custom form
if (typeof value === "object") {
for (var item in value) {
if (Array.isArray(value[item])) {
for (var arrayItem in value[item]) {
this.createFormParameters(form, (item + "[" + arrayItem + "]."), value[item][arrayItem]);
}
continue;
}
var input = document.createElement("textarea");
input.name = key + item;
input.value = value[item];
form.appendChild(input);
}
}
else
{
var input = document.createElement("textarea");
input.name = key;
input.value = value;
form.appendChild(input);
}
}

Resources