Unable to load css referenced assets with wkwebview - wkwebview

Swift 5 wkwebview appears to ignore requests for relative files from css referenced assets / images / fonts. inline nor .css files work.
This happens regardless if using the local bundle paths or a website stored in its own folder in the app documents.
Javascript runs fine, however all relative file calls in css fail.
I've set the baseURL to the appropriate folder, and many direct-linked files work fine. Just not css relative pathed referenced assets.
Background images and fonts do not load when referenced in a css file using relative file paths, example:
background-image: url("images/background.jpg");
background-image: url("/images/background.jpg");
All relative referenced files fail from css calls. Checked via safari debug of the application.
In the main bundle I have an www folder where the following folder structure is:
www/
www/index.html ( references all support files relative path - works )
www/css/style.css ( references image and font assets relative path - fails )
www/js/main.js
www/images/background.jpg
www/fonts/ { various fonts }
This exact folder is also copied to the app's document folder to test loading from the documents folder over a main bundle resource folder. Both fail.
I've tested and verified the above web app using a node http server, no issues.
In both cases, the actual HTML loads, and relative path javascript and css files load, javascript runs fine, however all files referenced within the style.css in relative path form do not load.
Using safari on the emulated iOS device I can see the working files that are referenced are given a system file reference for loading instead of relative paths, re:
file:///Users/{ username }/Library/Developer/CoreSimulator/Devices/ {...device serial number... }/data/Containers/Bundle/Application/{ ... application serial ... }/webtest.app/www/css/index.css
However the failed .css referenced files are referenced as:
file:///images/background.jpg
So baseURL doesn't seem to work or is ignored?
Example funcs - both do not work regarding this issue:
func loadLocalURL(refDir: String, entryPt: String, ext: String, param: String? = nil) -> Void {
let sUrl: String = entryPt + (param != nil ? "/?\(String(describing: param))" : "")
do {
let url = Bundle.main.url(
forResource: sUrl,
withExtension: ext,
subdirectory: refDir)
if (url != nil) {
let request = URLRequest(url: url!)
self.webView.load(request)
} else {
print("FAILED. URL: \(refDir)/\(sUrl).\(ext)")
}
} catch {
print("ERROR LOADING APP AT \(refDir)\(sUrl)\nerror: \(error) ")
return
}
}
func loadLocalDocsURL(url: URL, entryPoint: String) {
let folderURL = url.deletingPathExtension()
let htmlPath = folderURL.appendingPathComponent(entryPoint).path
let folderPath = folderURL.path
let baseUrl = URL(fileURLWithPath: folderPath, isDirectory: true)
let htmlURL = URL(fileURLWithPath: htmlPath, isDirectory: false)
self.webView.allowsBackForwardNavigationGestures = false
// self.webView.loadFileURL(htmlURL, allowingReadAccessTo: baseUrl) // Same asset relative path problem
self.webView.loadHTMLString(try! String(contentsOf: htmlURL), baseURL: htmlURL.deletingLastPathComponent()) // same relative path app problem
self.view = self.webView
}
Per documentation I expect that once a baseURL is set, the relative path requests should service just as if it were served via http service.

Related

React: add HTML from generic file path at server-side build time

The use case I'm trying to fulfill:
Admin adds SVG along with new content in CMS, specifying in the CMS which svg goes with which content
CMS commits change to git (Netlify CMS)
Static site builds again
SVG is added inline so that it can be styled and/or animated according to the component in which it occurs
Now - I can't figure out a clean way to add the SVG inline. My logic tells me - everything is available at build time (the svgs are in repo), so I should be able to simply inline the svgs. But I don't know how to generically tell React about an svg based on variables coming from the CMS content. I can import the svg directly using svgr/weback, but then I need to know the file name while coding, which I don't since it's coming from the CMS. I can load the svg using fs.readFileSync, but then the SVG gets lost when react executes client-side.
I added my current solution as an answer, but it's very hacky. Please tell me there's a better way to do this with react!
Here is my current solution, but it's randomly buggy in dev mode and doesn't seem to play well with next.js <Link /> prefetching (I still need to debug this):
I. Server-Side Rendering
Read SVG file path from CMS data (Markdown files)
Load SVG using fs.readFileSync()
Sanitize and add the SVG in React
II. Client-Side Rendering
Initial Get:/URL response contains the SVGs (ssr worked as intended)
Read the SVGs out of the DOM using HTMLElement.outerHTML
When React wants to render the SVG which it doesn't have, pass it the SVG from the DOM
Here is the code.
import reactParse from "html-react-parser";
import DOMPurify from "isomorphic-dompurify";
import * as fs from "fs";
const svgs = {}; // { urlPath: svgCode }
const createServerSide = (urlPath) => {
let path = "./public" + urlPath;
let svgCode = DOMPurify.sanitize(fs.readFileSync(path));
// add id to find the SVG client-side
// the Unique identifier is the filepath for the svg in the git repository
svgCode = svgCode.replace("<svg", `<svg id="${urlPath}"`);
svgs[urlPath] = svgCode;
};
const readClientSide = (urlPath) => {
let svgElement = document.getElementById(urlPath);
let svgCode = svgElement.outerHTML;
svgs[urlPath] = svgCode;
};
const registerSVG = (urlPath) => {
if (typeof window === "undefined") {
createServerSide(urlPath);
} else {
readClientSide(urlPath);
}
return true;
};
const inlineSVGFromCMS = (urlPath) => {
if (!svgs[urlPath]) {
registerSVG(urlPath);
}
return reactParse(svgs[urlPath]);
};
export default inlineSVGFromCMS;

How to add compressed scripts in Rust-Rocket?

I'm new in Rust and Rocket. I mount my static files in Rocket as follows:
.mount("/", StaticFiles::new("static", options))
It works good for images or styles but it's not quite what I expect from adding of scripts. I want to use compressed script files like *.js.gz instead of *.js. The HTML that links the script looks like:
<script type="text/javascript" src="/scripts/index.js.gz"></script>
How can I add headers to response in order to allow the use of compressed scripts? I assume I shouldn't use rocket_contrib::serve::StaticFiles for script files here.
In ASP.NET Core I do this as follows:
app.UseStaticFiles(new StaticFileOptions
{
OnPrepareResponse = content =>
{
if (!content.File.Name.EndsWith(".js.gz")) return;
content.Context.Response.Headers["Content-Type"] = "text/javascript";
content.Context.Response.Headers["Content-Encoding"] = "gzip";
}
});
Is there the same way for Rust-Rocket? Thanks!

express fileupload : no such file or directory

I am using express-file package. I use it like this:
const upload = require('express-fileupload');
app.use(upload({
createParentPath: true
}));
This is how I store:
await req.files.main_image.mv('./public/images/movies/'+main_image);
I already have public/images directory created. I don't have movies directory in public/images directory though.
When It works: If I have /public/images/movies directory already created, it works
When it doesn't work: If I don't have /public/images/movies directory created, but have /public/images directory. Then it says:
ENOENT: no such file or directory, open
'C:\Users\glagh\Desktop\Work\MoviesAdviser\public\images\movies\1554741546720-8485.11521.brussels.the-hotel-brussels.amenity.restaurant-AD3WAP2L-13000-853x480.jpeg
What to do so that it automatically creates /movies directory and put the image there?
Express-fileupload uses the following code to create the directory-path which you pass to the .mv function:
const checkAndMakeDir = function(fileUploadOptions, filePath){
//Check upload options were set.
if (!fileUploadOptions) return false;
if (!fileUploadOptions.createParentPath) return false;
//Check whether folder for the file exists.
if (!filePath) return false;
const parentPath = path.dirname(filePath);
//Create folder if it is not exists.
if (!fs.existsSync(parentPath)) fs.mkdirSync(parentPath);
return true;
};
The problem is that fs.mkdirSync() does not create the full path with the parent directories you specifiy (note that you'd use mkdir -p on the shell to create the whole folder structure) -> checkout How to create full path with node's fs.mkdirSync? for more information.
What you could do is use a module like fs-extra and use its function ensureDir which would create the corresponding parent directories (if they don't exist yet) before calling the .mv() function.
Or in case you're node version is >= 10 use the native {rescursive:true} option which you can pass to fs.mkdirSync.

Reactjs require image url in variable is not working

Strange problem
I'm dynamically including the path to an image in the source directory.
Putting the image directory in as a string works Fine (the part I commented out), but as soon as I put it in a variable it gives me the error " Cannot find module ".""
var imageDir="assets/img/MyImage.png";
--Working // const imageData= require('assets/img/MyImage.png');
--Not Working const imageData= require(imageDir);
Any one know why?
Same-ish problem Here
No answer unfortunately
Webpack needs to know what files to bundle during compile-time, but the real path value for expression(variable) only be given in runtime, you need require.context:
/* If the structure is like:
src -
|
-- index.js (where these codes are deployed)
|
-- assets -
|
--img
*/
let assetsPath = require.context('./assets/img', false, /\.(png|jpe?g|svg)$/);
// See where are the images after bundling.
// console.log(assetsPath('./MyImage.png'));
// You can put all the images you want in './assets/img', and access it.
var newElement = {
"id": doc.id,
"background": assetsPath('./MyImage.png');
};
If you wish use image on your react web application you can use next code
when use directly in html tag, but if you can use in part of java script, you must use const image = require('../Assets/image/03.jpg') and call letter this constant like this {image} between example tags

Expressjs file download - not downloading

I can't seem to get my "download file" feature working using Expressjs.
//DOWNLOAD FILE
router.get('/snippets/download', function (req, res) {
res.attachment("untitled.js");
res.send("here is some javascript");
});
If I access this route in my browser the file downloads to my computer but not if I use an Angularjs request to the route.
Am I missing something?
You can use res.download. Refer documentation here: http://expressjs.com/4x/api.html
Eg:
//DOWNLOAD FILE
router.post('/snippets/download', function (req, res) {
res.download(req.body.filename, req.body.text);
});
See if this helps.
The res.download() method need a file's full path ( which could be different in windows and linux with different seperator ).
And the 2nd param of res.download(localName, downloadPromptName ) should be able to modify the filename that user see (different from the file in your server's directory), but it seems that does not work in my environment.
So I recommend you to use res.sendFile(fullNameInServer ,options) where you can modify the downloaded filename in options.
var root = getDownloadRoot(req);
var options = {
root: getDownloadRoot(req),
headers: {
"content": "text/html;charset=utf-8",
"Content-Type": "application/octet-stream",
"Expires":"0",
"Cache-Control": "must-revalidate, post-check=0, pre-check=0",
"content-disposition": "attachment;filename=" + urlencode(downloadFilename)
}
};
res.sendFile( tempFileName ,options);
urlencode should be required to encode filename then you can use filename other than english.
before call download file , you need to write the file physically in a temp folder,
the getDownloadRoot() method give you the temp folder location in runtime which does not vary when you change the path to run the app.
here is the function getDownloadRoot()
function getDownloadRoot(req){
var path = require('path');
var sep = path.sep;
var parentPath = path.dirname(req.settings.views);
var ret = parentPath.concat(sep + tempFileFolder);
return ret;
}
For now I have no way other than using app.setting (that app is declared in app.js) to get the application folder during runtime. So I made a litte 'middleware' to transport the value with req object as following.
In app.js:
app.use(function(req, res, next) {
req.settings = app.settings;
next();
});
tempFileFolder is a folder that you can name it yourself.
sep is the folder seperator ( \ in windows and / in linux )
Also you need to watch the folder permission settings when running in linux.
This combination works perfectly in my environment (with angularjs)

Resources