Temporarily unzip a file to view contents within a browser - javascript

I want to unzip a file that contains an html page, css, and js directories. I want to unzip this temporarily and view the html in an iFrame, preferrably. I am using jszip which is working. I got the html to load, but how do I add the image, js, and css folders into the iFrame?
Here is what I have so far...
<div id="jszip_utils"></div>
<iframe id="iframe"></iframe>
<script type="text/javascript">
function showError(elt, err) {
elt.innerHTML = "<p class='alert alert-danger'>" + err + "</p>";
}
function showContent(elt, content) {
elt.innerHTML = "<p class='alert alert-success'>loaded !<br/>" +
"Content = " + content + "</p>";
}
var htmltext = JSZipUtils.getBinaryContent("/zip/myWebsite.zip", function (err, data) {
var elt = document.getElementById('jszip_utils');
if (err) {
showError(elt, err);
return;
}
try {
JSZip.loadAsync(data)
.then(function (zip) {
for(var name in zip.files) {
if (name.substring(name.lastIndexOf('.') + 1) === "html") {
return zip.file(name).async("string");
}
}
return zip.file("").async("string");
})
.then(function success(text) {
$('#iframe').contents().find('html').html(text);
showContent(elt, text);
}, function error(e) {
showError(elt, e);
});
} catch(e) {
showError(elt, e);
}
});
</script>
This gets the html, but the js css and image files are not showing up. I believe I need to do some sort of fake routing, but I'm not sure how I would be able to do that. Thanks for your help.

If the html/js in the zip is not too complicated, for instance an AngularJS app that has routes for partials, this is possible.
The trick is to replace css,js,img src/href urls that point to a file in the zip with either:
Object Url: URL.createObjectURL(Blob or File object);
Data Url: data:[<mediatype>][;base64],<data>
Or in the case of js and css inject the content directly into the appropriate element
After replacing the src/href references than just inject the new html into the iframe.
Step 1: Parse the html so you can manipulate it
//html from a call like zip.file("index.html").async("string")
let parser = new DOMParser;
let doc = parser.parseFromString(html,"text/html");
Step 2: Find all elements with a relative path (e.g. /imgs/img.jpg) as they are easier to deal with as you can then use that path for zip.file
//Simply finds all resource elements, then filters all that dont start with '/'
var elements = jQuery("link[href],script[src],img[src]",doc).filter(function(){
return /^\//.test(this.href || this.src);
});
Step 3: Replace src,href with object url, data url, or direct content
//assume element is the html element: <script src="/js/main.js"></script>
zip.file(element.src).async("string").then(jsText=>{
element.src = "data:text/javascript,"+encodeURIComponent(jsText);
});
Step 4: Get the new html text and inject it into the iframe
let newHTML = doc.documentElement.outerHTML;
var viewer = document.querySelector('#iframeID');
viewer = viewer.contentWindow || viewer.contentDocument.document || viewer.contentDocument;
viewer.document.open();
viewer.document.write(html);
viewer.document.close();
JSFiddle Demo - Demonstrates replacing the src/href urls
As a security note, if you are using zip files that you do not know the contents of, you should run the whole app in a protected iframe

Related

Redirecting after onclick()-event with a-tag

I am programming an app with elecrtonjs and got stuck.
So on my page there is this HTML:
<a href="./step1.html" id="next"><span onclick="writeToFile();return false">Next ></span></a`>
And in the javasriptfile I am saving stuff to a file in the directory:
var fs = require('fs');
function writeToFile() {
//alert(document.getElementsByTagName("input").length);
var text = "";
// Change the content of the file as you want
// or either set fileContent to null to create an empty file
for (var i = document.getElementsByTagName("input").length - 1; i >= 0; i--) {
text += document.getElementsByTagName("input")[i].getAttribute("name")+":\n";
text += document.getElementsByTagName("input")[i].value+"\n";
}
// The absolute path of the new file with its name
var filepath = "mynewfile.txt";
fs.appendFile(filepath, text, (err) => {
if (err) throw err;
console.log("The file was succesfully saved!");
});
}
The code redirects properly but does not append the user-input in to the specified file.
Am I timing the stuff wrong? I tried
onbeforeunload
Thanks for the help.
As Chris Li already stated, you should use
window.location.href = "./step1.html"
and remove the href field of your link.

jQuery.load doesn't work in ASP.NET MVC

I have this line in my Razor :
#Html.Raw(File.ReadAllText(Server.MapPath("~/Views/Home/index.html")))
And in HTML file, I have this :
<li>Personal Records</li>
And in my js file I have this :
if ($(link).text() === 'Personal Records') {
$("#govde").load("PersonalRecords.html");
}
But when I click on that link, nothing happens. When I open Index.html directly from file browser, it works. How can I fix this?
EDIT :
In console, it has this :
http://localhost:12345/PersonalRecords.html 404 (Not Found)
I guess I have placed the html files to a wrong folder. Can you tell me where to place? Thanks.
EDIT2 :
I have this in my JS :
var upperMenu = document.getElementById('upperMenu');
var requests = document.getElementById('requests');
$(upperMenu ).click(function (event) {
ustMenu.childNodes.forEach((myList) => {
$(myList).attr('class', ' ');
});
var link = event.target;
var list = link.parentNode;
$(myList).attr('class', 'active');
if ($(link).text() === 'Personal Records') {
$("#govde").load('#Url.Content("~/PersonalRecords.html")');
}
});
.load function is created in this(seperate) JS file.
The problem started with file name mentioned in $("#govde").load method:
$("#govde").load("PersonalRecords.html");
This statement tries to load "PersonalRecords.html" which assumed exists in the project's root directory, but it returns 404 since the target file exist in different directory.
Hence, it should be mentions full absolute path URL to load HTML content first:
var url = '#Url.Content("~/Views/Home/PersonalRecords‌​.html")';
Then, since load method placed inside separate JS file, putting them together should results like this:
Razor
<script src="#Url.Content("~/[path_to_your_JS_file]")" type="text/javascript"></script>
<script>
var url = '#Url.Content("~/Views/Home/PersonalRecords‌​.html")';
loadRequest(url);
</script>
JavaScript file
function loadRequest(url) {
var upperMenu = $("#upperMenu").get(0);
var requests = $("#requests").get(0);
$(upperMenu).click(function (event) {
ustMenu.childNodes.forEach((myList) => {
$(myList).attr('class', ' ');
});
var link = event.target;
var list = link.parentNode;
$(myList).attr('class', 'active');
if ($(link).text() === 'Personal Records') {
$("#govde").load(url);
}
}
}
Next, as of first mentioned part:
#Html.Raw(File.ReadAllText(Server.MapPath("~/Views/Home/index.html")))
I considered this is not a good practice to read all file contents in view side, hence I prefer return the file contents from controller side using FilePathResult like #Guruprasad Rao said:
// taken from /a/20871997 (Selman Genç)
[ChildActionOnly]
public ActionResult GetHtmlFile(String path)
{
// other stuff
// consider using Server.MapPath(path) if in doubt determining file path
return new FilePathResult(path, "text/html");
}
Usage as link in view:
<li>#Html.ActionLink("HTML File", "GetHtmlFile", "Controller", new { path = "~/Views/Home/PersonalRecords‌​.html" }, null)</li>
Similar issues:
Rendering .html files as views in ASP.NET MVC
Render HTML file in ASP.NET MVC view?

How to save the current webpage with casperjs/phantomjs?

Is there a way to save the current webpage by using casperjs or phantomjs?
I tried to get the html and save it into a file. But the resulting file was a lot different from the screenshot of that time (with casper.capture). Is there a way to save the current webpage?
Andrey Borisko suggested to use the disk cache to retrieve the resources. My solution is not that efficient, but you don't need to decompress text files.
I use XMLHttpRequest to retrieve all resources after I registered them with the resource.received event handler. I then filter the resources into images, css and fonts. The current limitation is that remote resource paths that contain something like ../ or ./ are not handled correctly.
I retrieve the current page content with getHTML and iterate over all captured resources to replace the path used in the markup, that is identified by a portion of the complete resource URL, with a randomly generated file name. The file extension is created from the content type of the resource. It is converted using mimeType from this gist.
Since CSS files may contain background images or fonts, they have to be processed before saving to disk. The provided loadResource function loads the resource, but does not save it.
Since XMLHttpRequest to download the resources the script has to be invoked with the --web-security=false flag:
casperjs script.js --web-security=false
script.js
var casper = require("casper").create();
var utils = require('utils');
var fs = require('fs');
var mimetype = require('./mimetype'); // URL provided below
var cssResources = [];
var imgResources = [];
var fontResources = [];
var resourceDirectory = "resources";
var debug = false;
fs.removeTree(resourceDirectory);
casper.on("remote.message", function(msg){
this.echo("remote.msg: " + msg);
});
casper.on("resource.error", function(resourceError){
this.echo("res.err: " + JSON.stringify(resourceError));
});
casper.on("page.error", function(pageError){
this.echo("page.err: " + JSON.stringify(pageError));
});
casper.on("downloaded.file", function(targetPath){
if (debug) this.echo("dl.file: " + targetPath);
});
casper.on("resource.received", function(resource){
// don't try to download data:* URI and only use stage == "end"
if (resource.url.indexOf("data:") != 0 && resource.stage == "end") {
if (resource.contentType == "text/css") {
cssResources.push({obj: resource, file: false});
}
if (resource.contentType.indexOf("image/") == 0) {
imgResources.push({obj: resource, file: false});
}
if (resource.contentType.indexOf("application/x-font-") == 0) {
fontResources.push({obj: resource, file: false});
}
}
});
// based on http://docs.casperjs.org/en/latest/modules/casper.html#download
casper.loadResource = function loadResource(url, method, data) {
"use strict";
this.checkStarted();
var cu = require('clientutils').create(utils.mergeObjects({}, this.options));
return cu.decode(this.base64encode(url, method, data));
};
function escapeRegExp(string) {
// from https://stackoverflow.com/a/1144788/1816580
return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1");
}
function replaceAll(find, replace, str) {
// from https://stackoverflow.com/a/1144788/1816580
return str.replace(find, replace);
}
var wrapFunctions = [
function wrapQuot1(s){
return '"' + s + '"';
},
function wrapQuot2(s){
return "'" + s + "'";
},
function csswrap(s){
return '(' + s + ')';
}
];
function findAndReplace(doc, resources, resourcesReplacer) {
// change page on the fly
resources.forEach(function(resource){
var url = resource.obj.url;
// don't download again
if (!resource.file) {
// set random filename and download it **or** call further processing which in turn will load ans write to disk
resource.file = resourceDirectory+"/"+Math.random().toString(36).slice(2)+"."+mimetype.ext[resource.obj.contentType];
if (typeof resourcesReplacer != "function") {
if (debug) casper.echo("download resource (" + resource.obj.contentType + "): " + url + " to " + resource.file);
casper.download(url, resource.file, "GET");
} else {
resourcesReplacer(resource);
}
}
wrapFunctions.forEach(function(wrap){
// test the resource url (growing from the back) with a string in the document
var lastURL;
var lastRegExp;
var subURL;
// min length is 4 characters
for(var i = 0; i < url.length-5; i++) {
subURL = url.substring(i);
lastRegExp = new RegExp(escapeRegExp(wrap(subURL)), "g");
if (doc.match(lastRegExp)) {
lastURL = subURL;
break;
}
}
if (lastURL) {
if (debug) casper.echo("replace " + lastURL + " with " + resource.file);
doc = replaceAll(lastRegExp, wrap(resource.file), doc);
}
});
});
return doc;
}
function capturePage(){
// remove all <script> and <base> tags
this.evaluate(function(){
Array.prototype.forEach.call(document.querySelectorAll("script"), function(scr){
scr.parentNode.removeChild(scr);
});
Array.prototype.forEach.call(document.querySelectorAll("base"), function(scr){
scr.parentNode.removeChild(scr);
});
});
// TODO: remove all event handlers in html
var page = this.getHTML();
page = findAndReplace(page, imgResources);
page = findAndReplace(page, cssResources, function(cssResource){
var css = casper.loadResource(cssResource.obj.url, "GET");
css = findAndReplace(css, imgResources);
css = findAndReplace(css, fontResources);
fs.write(cssResource.file, css, "wb");
});
fs.write("page.html", page, "wb");
}
casper.start("http://www.themarysue.com/").wait(3000).then(capturePage).run(function(){
this.echo("DONE");
this.exit();
});
The magic happens in findAndReplace. capturePage is completely synchronous so it can be dropped anywhere without much head ache.
URL for mimetype.js
No, I don't think there is an easy way to do this as phantomjs doesn't support rendering pages in mht format (Render as a .mht file #10117). I believe that's what you wanted.
So, it needs some work to accomplish this. I did something similar, but i was doing it the other way around I had a rendered html code that I was rendering into image/pdf through phantomjs. I had to clean the file first and it worked fine for me.
So, what I think you need to do is:
strip all js calls, like script tags or onload attributes, etc..
if you have access from local to the resources like css, images and so on (and you don't need authentication to that domain where you grab the page) than you need to change relative paths of src attributes to absolute to load images/etc.
if you don't have access to the resources when you open the page then I think you need to implement similar script to download those resources at the time phantomjs loads the page and then redirect src attributes to that folder or maybe use data uri.
You might need to change links in css files as well.
This will bring up the images\fonts and styling you are missing currently.
I'm sure there are more points. I'll update the answer if you need more info, once I see my code.

How can I iterate through all elements of local (server-side) folder?

Basically, I have a very simple website where the root directory looks like:
/images/
index.html
stuff.js
I want some way to recursively iterate through every file in the /images/ directory and display them in order in a section of my website. So for example, if /images/ contained:
images/a/a.png
images/b.png
images/c.jpg
....
then somewhere in index.html would contain:
<img src="images/a/a.png" />
<img src="images/b.png" />
<img src="images/c.jpg" />
....
My first idea was to do this using the document.write() function in stuff.js, but I couldn't find a good way to iterate through the local file directory in Javascript. I saw something about AJAX, but all of those examples involved editing an existing file, which I obviously don't want to do.
My current solution is just to manual create an array of strings containing all of the files in /images/, but doing this makes me think "There's got to be a better way!"
Let me know if I've been unclear.
Thanks!
Perhaps the best way to do this is to use a server-sided language to do it for you, and to use an asynchronous Javascript request to display the data.
This sample uses PHP to list all the files in a specified directory, and an xmlhttprequest to load this output and convert the results into image tags:
getimages.php:
<?php
//The directory (relative to this file) that holds the images
$dir = "Images";
//This array will hold all the image addresses
$result = array();
//Get all the files in the specified directory
$files = scandir($dir);
foreach($files as $file) {
switch(ltrim(strstr($file, '.'), '.')) {
//If the file is an image, add it to the array
case "jpg": case "jpeg":case "png":case "gif":
$result[] = $dir . "/" . $file;
}
}
//Convert the array into JSON
$resultJson = json_encode($result);
//Output the JSON object
//This is what the AJAX request will see
echo($resultJson);
?>
index.html (same directory as getimages.php):
<!DOCTYPE html>
<html>
<head>
<title>Image List Thing</title>
</head>
<body>
<div id="images"></div>
<input type="button" onclick="callForImages()" value="Load" />
<script>
//The div element that will contain the images
var imageContainer = document.getElementById("images");
//Makes an asynch request, loading the getimages.php file
function callForImages() {
//Create the request object
var httpReq = (window.XMLHttpRequest)?new XMLHttpRequest():new ActiveXObject("Microsoft.XMLHTTP");
//When it loads,
httpReq.onload = function() {
//Convert the result back into JSON
var result = JSON.parse(httpReq.responseText);
//Show the images
loadImages(result);
}
//Request the page
try {
httpReq.open("GET", "getimages.php", true);
httpReq.send(null);
} catch(e) {
console.log(e);
}
}
//Generates the images and sticks them in the container
function loadImages(images) {
//For each image,
for(var i = 0; i < images.length; i++) {
//Make a new image element, setting the source to the source in the array
var newImage = document.createElement("img");
newImage.setAttribute("src", images[i]);
//Add it to the container
imageContainer.appendChild(newImage);
}
}
</script>
</body>
</html>
Note that this is only an example. You'll probably want to make sure that the AJAX call is successful, and that the JSON conversion works both in the server code and on the client.
I stumbled on this article, as I was looking for the same thing, how to iterate through a list of files in a "Resources" folder, and display a webpage with clickable shortcuts to each of them.
Here's a clip of the webpage I ended up with:
Here's how I did it.
I added a very simple ASP.Net service, to iterate through the files in this folder...
List<OneResourceFile> listOfFilenames = new List<OneResourceFile>();
string Icon = "";
string localFolder = Server.MapPath("../Resources");
string[] fileEntries = Directory.GetFiles(localFolder);
foreach (string fileName in fileEntries)
{
string filename = System.IO.Path.GetFileName(fileName);
switch (Path.GetExtension(filename).ToLower())
{
case ".pptx":
case ".ppt":
Icon = "cssPowerPoint";
break;
case ".doc":
case ".docx":
Icon = "cssWord";
break;
case ".xlsx":
case ".xlsm":
case ".xls":
Icon = "cssExcel";
break;
default:
Icon = "cssUnknown";
break;
}
OneResourceFile oneFile = new OneResourceFile()
{
Filename = filename,
IconClass = Icon,
URL = "../Resources/" + filename
};
listOfFilenames.Add(oneFile);
}
string JSON = JsonConvert.SerializeObject(listOfFilenames);
return JSON;
..which built up a List of OneResouceFile records, each with a Filename, a CSS Class to apply to that shortcut (which would give it, say, an Excel icon, a PDF icon, etc) and a full URL of the item.
public class OneResourceFile
{
public string Filename { get; set; }
public string IconClass { get; set; }
public string URL { get; set; }
}
..and which returned a JSON set of results like this...
[
{
Filename: "Mikes Presentation.pptx",
IconClass: "cssPowerPoint",
URL: "~/Resources/Mikes Presentation.pptx"
},
{
Filename: "Mikes Accounts.xlsx",
IconClass: "cssExcel",
URL: "~/Resources/Mikes Accounts.xlsx""
}
]
Then, I just got some JQuery to call this web service, and create a a href for each item in the results:
<script type="text/javascript">
var URL = "/GetListOfResourceFiles.aspx"; // This is my web service
$.ajax({
url: URL,
type: 'GET',
cache: false,
dataType: "json",
success: function (JSON) {
// We've successfully loaded our JSON data
$.each(JSON.Results, function (inx) {
// Create one <a href> per JSON record, and append it to our list.
var thelink = $('<a>', {
text: this.Filename,
title: this.Filename,
href: this.URL,
class: this.IconClass
}).appendTo('#ListOfResources');
});
},
error: function (xhr, ajaxOptions, thrownError) {
alert("$.ajax error: " + xhr.status + " " + thrownError);
}
});
</script>
<p id="ListOfResources">
All you need then is to add some CSS styling for cssPowerPoint, cssExcel, etc, to give the a hrefs a relevant icon, for example:
.cssPowerpoint
{
vertical-align: top;
text-align: center;
background-repeat: no-repeat;
background-position: center 5px;
background-image: url(/Images/Icons/icnPowerPoint.png);
width: 100px;
height: 60px;
padding-top: 60px;
text-decoration: none;
display:inline-block;
color: #666;
margin-left: 20px;
}
And that's it. Cool, hey ?

How do you access other files stored on an website with JavaScript?

I am trying to write a webpage for a list of files to download. The files are stored with the webpage and I want the webpage to dynamically list all the files in the folder to download. That way when more are added I don't have to modify the webpage. I know how to use JavaScript to create links on the webpage but I need to use it to find the names of the files first.
I found a website that had code for navigating files like a file browser but it only uses a string to store the current location.
This is in the header:
<script type="text/javascript"><!--
var myloc = window.location.href;
var locarray = myloc.split("/");
delete locarray[(locarray.length-1)];
var fileref = locarray.join("/");
//--></script>
this is in the body:
<form>
<input type=button value="Show Files" onClick="window.location=fileref;">
</form>
However this doesn't really help since I am trying to create download links to files not have a file browser.
Edit:
When you host a traditional HTML page you upload the htmlfile and any images or content for the page to what ever server you use.
I want to use javascript to dynamically link to every file hosted with the webpage.
I am trying to combine this with hosting the files in a Dropbox public folder for a simple way to make the files available.
If you want a list of files on the server you will need to use a server-side script to gather their names:
JS--
//use AJAX to get the list of files from a server-side script
$.getJSON('path/to/server-side.php', { 'get_list' : 'true' }, function (serverResponse) {
//check the response to make sure it's a success
if (serverResponse.status == 'success') {
var len = serverResponse.output.length,
out = [];
//iterate through the serverResponse variable
for (var i = 0; i < len; i++) {
//add output to the `out` variable
out.push('<li>' + serverResponse.output[i] + '</li>');
}
//place new serverResponse output into DOM
$('#my-link-container').html('<ul>' + out.join('') + '</ul>');
} else {
alert('An Error Occured');
}
});
PHP--
<?php
//check to make sure the `get_list` GET variable exists
if (isset($_GET['get_list'])) {
//open the directory you want to use for your downloads
$handle = opendir('path/to/directory');
$output = array();
//iterate through the files in this directory
while ($file = readdir($handle)) {
//only add the file to the output if it is not in a black-list
if (!in_array($file, array('.', '..', 'error_log'))) {
$output[] = $file;
}
}
if (!empty($output)) {
//if there are files found then output them as JSON
echo json_encode(array('status' => 'success', 'output' => $output));
} else {
//if no files are found then output an error msg in JSON
echo json_encode(array('status' => 'error', 'output' => array()));
}
} else {
//if no `get_list` GET variable is found then output an error in JSON
echo json_encode(array('status' => 'error', 'output' => array()));
}
?>

Categories

Resources