I am using Angular-cli for this build and it compiles all folders under the src folder into the build.
I was storing images in the assets folder under src:
src
|-app
|-assets
|-img_library
I access them dynamically like this:
<img src="assets/img_library/{{imgId}}"
Unfortunately this folder gets compiled into the build by angular-cli, so I would have to rebuild the app every time an image is added for the client to be able to access it.
I built the server as well so I can store the images anywhere I want but I don't know how to access folders above the src via a img tag.
Is there a way to access a public/assets folder above the src folder with an image tag in Angular?
#jonrsharpe You're right this doesn't make any sense. The assets folder is for images/media that will be used often by most users. I don't know what I was thinking, my brain was stuck in Angular mode when I needed to approach it from the backend.
I used an express api:
router.get('/some/api/:id/img.png', function( req, res, next){
var id = req.params.id,
filePath = 'img.png',
root = __dirname + '/some/location/' + id +'/';
var options = {
root: root,
dotfiles: 'deny',
headers: {
'x-timestamp': Date.now(),
'x-sent': true
}
};
res.sendFile(filePath, options, (err) => {
if (err) {
next(err);
} else {
console.log('Sent:', filePath);
}
});
})
to respond to a get request from <img src='some/api/{{imageId}}/img.png'>.
Hope this helps some other sleep deprived developer.
Related
In my NuxtJS application I has a folder with html pages, that can be added/deleted in any time from outside (/static/pages/page1.html, /static/pages/page2.html, ...) and I got a mapping to real uri's for this pages
{ '/foo': 'page1.html', '/bar': 'page2.html', ... }
I know I can use #nuxtjs/proxy, but it requires to rebuild an app every time mapping changes. I also know I can use nginx's rewrites for this, but changing it's config every time is painful too.
I also tried using 'pages/_.vue' file, read .html in component and place it's content to html using v-html, but files contains full html page (w/ scripts), and nuxt throw and error in this case, 'cos v-html don't allow using js (or maybe another reasons, which I can't understand)
How can I make dynamic proxy for this in NuxtJS?
For someone looking for answer for same question
Solve this by creating simple server middleware
in /pages_proxy/index.js:
const path = require('path');
const { Router } = require('express');
const express = require('express')
const app = express()
const router = Router()
router.get('*', async (req, res, next) => {
const pages = { '/foo/': 'page1.html', '/bar/': 'page2.html', ... }
const page = pages[req.path];
if (page) {
res.sendFile(path.join(__dirname, '../static/pages', page));
} else {
next();
}
});
app.use(router)
module.exports = app
in nuxt.config.js
serverMiddleware: {
'/': '~/pages_proxy'
},
I am using minio to manage the files
const getMinioClient = () => {
const minioClient = new Minio.Client({
endPoint: '127.0.0.1',
port: 9000,
useSSL: false,
accessKey: 'minioadmin',
secretKey: 'minioadmin'
});
return minioClient;
};
uploadFile(bucketName, newFileName, localFileLocation,metadata={}) {
return new Promise((resolve, reject) => {
const minioClient = getMinioClient();
//'application/octet-stream'
minioClient.fPutObject(bucketName, newFileName, localFileLocation, metadata , (err, etag) => {
if (err) return reject(err);
return resolve(etag);
});
});
}
with the following code I can upload the file, after successfully uploading it returns me only with etag, but I want to get the download link, how would I get it directly without searching the filename again.
You won't be able to get something like Public URL/Link for accessing images unless you ask for it to manually generate a time limited download URL using something like:
https://min.io/docs/minio/linux/reference/minio-mc/mc-share-download.html#generate-a-url-to-download-object-s
One workaround is to let nginx directly access the location you are uploading your files to:
https://gist.github.com/harshavardhana/f05b60fe6f96803743f38bea4b565bbf
After you have successfully written your file with your code above, you can use presignedUrl method to generate the link to your image.
An example for Javascript is here: https://min.io/docs/minio/linux/developers/javascript/API.html#presignedUrl:~:text=//%20presigned%20url%20for%20%27getObject%27%20method.%0A//%20expires%20in%20a%20day.%0AminioClient.presignedUrl(%27GET%27%2C%20%27mybucket%27%2C%20%27hello.txt%27%2C%2024*60*60%2C%20function(err%2C%20presignedUrl)%20%7B%0A%20%20if%20(err)%20return%20console.log(err)%0A%20%20console.log(presignedUrl)%0A%7D)
In any case you have to set an expiration time. Here or you set a very long time, which is suitable to your app or if you have a backend, require the images from Frontend through the backend with the getObject method: getObject(bucketName, objectName, getOpts[, callback]).
https://min.io/docs/minio/linux/developers/javascript/API.html#presignedUrl:~:text=getObject(bucketName%2C%20objectName%2C%20getOpts%5B%2C%20callback%5D)
If you have only a few number of static images to show in your app, (which are not uploaded by your app), you can also create the links manually with tme minio client or from the Minio-UI.
Problem
If I load the page without a service worker, everything is fine but when I introduce a service worker, the page does not load the first time (when online) because it is missing my bundled files which causes both CSS and Script issues. If I refresh the page then everything works because the service worker caches fetch requests when they occur.
Setup
Say I have a bundle such as
bundles.Add(new ScriptBundle("~/js/main").Include(
"~/Scripts/jquery-3.3.1.min.js",
"~/Scripts/moment.min.js",
"~/node_modules/sweetalert/dist/sweetalert.min.js"));
and a service worker in my root directory such as
var cacheName = 'v1:static';
self.addEventListener('install', function (e) {
e.waitUntil(
caches.open(cacheName).then(function (cache) {
return cache.addAll([
'/images/keypad/number 0.png',
'/',
'/Menu/MainMenu',
'**TODO: Cache Bundles**'
]).then(function () {
self.skipWaiting();
});
})
);
});
self.addEventListener('fetch', function (event) {
if (event.request.method != "POST") {
event.respondWith(
caches.match(event.request).then(function (response) {
if (response) {
return response;
}
fetch(event.request).then(function (fetchResponse) {
caches.open(cacheName).then(function (cache) {
cache.put(event.request, fetchResponse.clone());
});
return fetchResponse;
});
})
);
}
});
Question
How do I cache my bundled files in my service worker, in both Debug and Release mode because debug displays them as individual files while Release will combine them, and I don't know the URLs to the bundles?
Additional Thoughts
You can't put a Razor #... in this file because it isn't cshtml.
I don't want to list every file out in both places and maintain both.
I thought about using a server side handler to generate my service-worker.js file but I was wondering if there is an actual clean way to do this without going crazy.
Thank You!
#Scripts.Url("~/bundles/yourBundleName") use this to get the absolute URL for the URL from there you can get the bundle name map that into a variable and use that variable in service worker install event so that assets are cached on load itself.
note:
one mistake which i used to do Also please don’t refer serviceworker.js file directly in to index.html, this will be loaded at the time of registering from sw-registration.js file
I have a multi-step form in Vue, I am posting the results once I collect all of the information, to a Laravel controller. This is an authenticated area of the site. I am using Passport. So essentially I have a Vue SPA that is the admin area of a website built within the Laravel 5.7 framework.
Vue file:
axios.post('/api/quotes/finalize', this.wizardModel)
.then(response => {
if (response.data.success) {
//
}
})
.catch(err => {
if (err.response.status == 401) {
window.location.href = '/login';
}
swal.showValidationError(
`Request failed: ${error}`
)
})
The controller gets data and makes a pdf. All of that is working. It then has three actions to look at - email the PDF, email the PDF with CC, and download the PDF.
public function finalizeQuote(Request $request)
{
$data = $request->all();
$data['phone'] = $this->formatTelephone($data['phone']);
$dateStamp = date('Ymdhis', strtotime('now'));
$fileName = 'quote-' . $dateStamp . '.pdf';
$html = View::make('quotes.quote', compact('data'))->render();
$conv = new Converter();
$conv->addPage($html)
->save('storage/' . $fileName);
if ($data['actions']['emailPDF']) {
$message = (new Proposal('storage/' . $fileName))
->onConnection('redis')
->onQueue('emails');
if ($data['actions']['carbonCopy']) {
Mail::to($data['emailAddress'])
->cc(Auth::user()->email)
->queue($message);
} else {
Mail::to($data['emailAddress'])->queue($message);
}
}
if ($data['actions']['downloadPDF']) {
return response()->download(public_path('storage/' . $fileName));
}
}
So in dev tools I see the pdf file in the response. No download occurs in the browser. I am sure I am just missing something fundamental here.
Ajax requests alone cannot invoke a download. You could have a hidden form that posts your data to your api endpoint, probably the quickest solution.
Axios has a built in way to do this easily
axios({
method: 'post',
url: '/api/quotes/finalize',
data: wizardModelFormData,
config: { headers: {'Content-Type': 'multipart/form-data' }}
})
Instead of sending json in the request, you would have to send form data. It is pretty easy to setup a form data object, and a quick google on FormData should be enough.
Another way is to do something like this. Have the endpoint return a download url for your file,
if ($data['actions']['downloadPDF']) {
return response()->json(["download_url" => "/api/download/" . $fileName]);
}
and on the client side, use window.location to set the browser location to this api endpoint that only returns files (in this case /api/download/{fileName}). The browser would then download any file returned from that location you set (if you have the headers setup correctly).
This would mean you would have to implement the /api/download/{fileName} route, but that is where you would put something like this.
return response()->download(public_path('storage/' . $fileName));
Obviously that is just a simple explanation and it doesn't need to be implemented exactly the same way, but it gets the idea across.
Now you would want to make sure that you don't put any sensitive files in storage/ and have some permissions setup.
Axios is used for XHR requests. Typically, downloading files is done through normal GET requests but here you have an XHR post request to download files.
To download files over AJAX, some folks use libraries and some create
a blob link from the response, append it to DOM and then, trigger a
click on the link.
You can refer this gist and SO post for further details.
Personally, I prefer simple non-XHR get requests for file downloads.
I've built my first full React app, and it works beautifully. I load my dev-server and everything loads exactly as I would hope. Notably, my image files load perfectly.
An example of a successful image load on my dev server:
<img src='./img/sq/filename.jpg' />
When running the dev server, when I inspect this element in the console and go to 'Sources', I see the file tree like this:
- localhost:3333
- css [folder]
- js [folder]
- img/sq [folder]
- filename.jpg
- index [file]
This is as I would expect.
However, when running the production server, the image fails to load. When I inspect the element, and go to 'Sources', this is what I see instead:
- localhost:3000
- static [folder]
- css [folder]
- js [folder]
- index [file]
So my production build is apparently ignoring the /img/sq folder altogether.
I've heard about file-loader and url-loader, but I've yet to find a clear and simple explanation of what they do and how to use them. I've installed both to my dependencies successfully, and I've added this loader to my webpack.config.js file:
{
test: /\.(jpe?g|png|gif|svg)$/i,
loaders: [
'file?hash=sha512&digest=hex&name=[hash].[ext]',
'image-webpack?bypassOnDebug&optimizationLevel=7&interlaced=false'
]
}
After doing this, I'm not sure how this is supposed to change the way the images load. Should I change the way the image is referenced? Any help/pointers would be massively appreciated, as this has me stumped.
UPDATE:
This is my server.js file. I can't see any radical difference between the code that you posted and my own, except for me using res.render method, to your res.sendFile.
Is there anything I may have missed here?
server.js
var express = require('express')
var app = express();
app.use('/static', express.static(__dirname + '/static'));
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.get('*', function (req, res) {
res.render("index");
});
const port = process.env.PORT || 3000;
const env = process.env.NODE_ENV || 'production';
app.listen(port, err => {
if (err) {
return console.error(err);
}
console.info(`Server running on http://localhost:${port} [${env}]`);
});
I recommend you using the 'Create React App' as a skeleton.
https://github.com/facebookincubator/create-react-app
You can add an image to the App.js that is created to test if the image loads in both your development and production servers.
our app class with the added image should look like this:
class App extends Component {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
<img src='./img/sq/filename.jpg' />
</div>
);
}
}
As an example, you can successfully load an image with node.js server with this code (file index.js) and the build folder with the react code
const express = require('express');
const path = require('path');
const app = express();
app.use(express.static('./build'));
app.get('/', function (req, res) {
res.sendFile(path.join(__dirname, './build', 'index.html'));
});
app.listen(9000);
To summarize the steps are:
Use the 'Create React App' to create a skeleton for you application
Add a single image to the render code (use the above code)
Run npm run build to create the build folder with the image in it
Use the node.js code to create a server (use above code) and put your build folder (created in the previous step) next to your index.js node code.
Remark: you should install 'express' 'path' modules and then run your server node.js code.
npm install express --save
npm install path --save
node index.js
Navigate to localhost:9000 to see your app with the image in it.
These steps should help you resolve your issue as it is an example of how to build react code to be used in production and see the image in a production environment.