写点什么

还能把浏览器当作 Web 服务器?骚操作,学废了~

作者:掘金安东尼
  • 2022 年 9 月 13 日
    广东
  • 本文字数:4499 字

    阅读完需:约 15 分钟

还能把浏览器当作 Web 服务器?骚操作,学废了~

楔子

什么?还能把浏览器当作 Web 服务器?


闲话少说,直接干货!


整体思路:PWA 中用于缓存文件的 server workers 可以动态生成新文件,并通过 fetch 事件,将它们发送至浏览器!


  • 不熟悉 PWA 的朋友们可简单了解如下:


PWA(Progressive Web Apps) 翻译为 渐进式网页应用,它是一种构建 Web 应用程序的新理念,涉及 一些 特定的模式,API 和其他功能。


它能实现传统 web 所不能做到的:离线工作、可安装、易于同步、可以发送推送通知等;


  • 不熟悉 server workers 的朋友们可简单了解如下:


server workers 就是一个服务器与浏览器之间的中间人角色,如果网站中注册了 service worker,那么它可以拦截当前网站所有的请求,进行判断(需要编写相应的判断程序),如果需要向服务器发起请求的就转给服务器,如果可以直接使用缓存的就直接返回缓存不再转给服务器。从而大大提高浏览体验。


所以,通过 server workers 可以发送文件至浏览器!

工具


somorphic-git 是 git 的纯 JavaScript 实现,适用于 Node 和浏览器环境(包括 WebWorkers 和 ServiceWorkers);


它可以用于读写 git 库,以及从 Github 获取和推送。



BrowserFS 与 Webpack 类似,也是模块打包工具;


它的特点:


  1. 基于流式(stream)思想设计

  2. 可以通过 command line,也可以通过 API 来使用

  3. 仅处理 javascript

  4. 模块化的逆过程,但是推动着模块化的更好发展

  5. 内置了一些 node core module

  6. node 模块可以在浏览器端使用,是 同构应用 的有力武器



它让浏览器文件读写更加快速,快如闪电⚡~



这个相信大家并不陌生,indexedDB 可以实现在客户端存储大量的结构化数据~~

实现

代码实现:


/**@license *   ___ ___ _____  __      __   _      _____              _           _ *  / __|_ _|_   _| \ \    / /__| |__  |_   _|__ _ _ _ __ (_)_ _  __ _| | * | (_ || |  | |    \ // / -_) '_ \   | |/ -_) '_| '  | | ' / _` | | *  ___|___| |_|     _/_/___|_.__/   |_|___|_| |_|_|_|_|_||___,_|_| * * this is service worker and it's part of GIT Web terminal * * Copyright (c) 2018 Jakub Jankiewicz <http://jcubic.pl/me> * Released under the MIT license * *//* global BrowserFS, Response, setTimeout, fetch, Blob, Headers */self.importScripts('https://cdn.jsdelivr.net/npm/browserfs');
self.addEventListener('install', self.skipWaiting);
self.addEventListener('activate', self.skipWaiting);
self.addEventListener('fetch', function (event) { let path = BrowserFS.BFSRequire('path'); let fs = new Promise(function(resolve, reject) { BrowserFS.configure({ fs: 'IndexedDB', options: {} }, function (err) { if (err) { reject(err); } else { resolve(BrowserFS.BFSRequire('fs')); } }); }); event.respondWith(fs.then(function(fs) { return new Promise(function(resolve, reject) { function sendFile(path) { fs.readFile(path, function(err, buffer) { if (err) { err.fn = 'readFile(' + path + ')'; return reject(err); } var ext = path.replace(/.*./, ''); var mime = { 'html': 'text/html', 'json': 'application/json', 'js': 'application/javascript', 'css': 'text/css' }; var headers = new Headers({ 'Content-Type': mime[ext] }); resolve(new Response(buffer, {headers})); }); } var url = event.request.url; var m = url.match(/__browserfs__(.*)/); function redirect_dir() { return resolve(Response.redirect(url + '/', 301)); } function serve() { fs.stat(path, function(err, stat) { if (err) { return resolve(textResponse(error404Page(path))); } if (stat.isFile()) { sendFile(path); } else if (stat.isDirectory()) { if (path.substr(-1, 1) !== '/') { return redirect_dir(); } fs.readdir(path, function(err, list) { if (err) { err.fn = 'readdir(' + path + ')'; return reject(err); } var len = list.length; if (list.includes('index.html')) { sendFile(path + '/index.html'); } else { listDirectory({fs, path, list}).then(function(list) { resolve(textResponse(fileListingPage(path, list))); }).catch(reject); } }); } }); } if (m) { var path = m[1]; if (path === '') { return redirect_dir(); } console.log('serving ' + path + ' from browserfs'); serve(); } else { if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') { return; } //request = credentials: 'include' fetch(event.request).then(resolve).catch(reject); } }); }));});// -----------------------------------------------------------------------------function listDirectory({fs, path, list}) { return new Promise(function(resolve, reject) { var items = []; (function loop() { var item = list.shift(); if (!item) { return resolve(items); } fs.stat(path + '/' + item, function(err, stat) { if (err) { err.fn = 'stat(' + path + '/' + item + ')'; return reject(err); } items.push(stat.isDirectory() ? item + '/' : item); loop(); }); })(); });}
// -----------------------------------------------------------------------------function textResponse(string, filename) { var blob = new Blob([string], { type: 'text/html' }); return new Response(blob);}
// -----------------------------------------------------------------------------function fileListingPage(path, list) { var output = [ '<!DOCTYPE html>', '<html>', '<body>', `<h1>BrowserFS ${path}</h1>`, '<ul>' ]; if (path.match(/^/(.*/)/)) { output.push('<li><a href="..">..</a></li>'); } list.forEach(function(name) { output.push('<li><a href="' + name + '">' + name + '</a></li>'); }); output = output.concat(['</ul>', '</body>', '</html>']); return output.join('\n');}
// -----------------------------------------------------------------------------function error404Page(path) { var output = [ '<!DOCTYPE html>', '<html>', '<body>', '<h1>404 File Not Found</h1>', `<p>File ${path} not found in browserfs`, '</body>', '</html>' ]; return output.join('\n');}
复制代码


初始化 service worker 如下:


<pre class="js jt ju jv jw ll hj du"><span id="0644" class="bx lm kg fs ln b bu lo lp s lq" data-selectable-paragraph=""><strong class="ln lh">if</strong> ('serviceWorker' <strong class="ln lh">in</strong> navigator) {<br> <strong class="ln lh">var</strong> scope <strong class="ln lh">=</strong> location.pathname.replace(//[^/]+$/, '/');<br> <strong class="ln lh">if</strong> (<strong class="ln lh">!</strong>scope.match(/browserfs/)) {<br> navigator.serviceWorker.register('sw.js', {scope})<br> .then(<strong class="ln lh">function</strong>(reg) {<br> reg.addEventListener('updatefound', function() {<br> <strong class="ln lh">var</strong> installingWorker <strong class="ln lh">=</strong> reg.installing;<br> console.log('A new service worker is being installed:',<br> installingWorker);<br> });<br> <em class="lr">// registration worked</em><br> console.log('Registration succeeded. Scope is ' <strong class="ln lh">+<br> </strong>reg.scope);<br> }).<strong class="ln lh">catch</strong>(function(error) {<br> <em class="lr">// registration failed</em><br> console.log('Registration failed with ' <strong class="ln lh">+</strong> error);<br> });<br> }<br>}</span></pre>

测试

原作者大佬 Jakub T. Jankiewicz 还搞了个在线测试 Demo GIT Web Terminal,除了 NB 我还能说什么呢?


调用测试:


vi test.txt
i Hello World :wq
view test.txt
复制代码


查看返回:



有一说一,这个方向的尝试还是很顶的~~


增强 Web 能力,吾辈义不容辞!(●'◡'●)


<hr>


OK,以上就是本篇分享~ 撰文不易,点赞鼓励👍👍👍


本篇参考:



我是掘金安东尼,公众号同名,日拱一卒、日掘一金,再会~

发布于: 刚刚阅读数: 4
用户头像

安东尼陪你度过漫长编程岁月~ 2022.07.14 加入

社会我瓜哥,人狠话不多😎 微信 anthony1453,加我交个朋友😎 正联合【机械工业出版社】出版《程序员成长手册》,敬请期待😎 真正的大师,永远怀着一颗学徒的心(易)😎

评论

发布
暂无评论
还能把浏览器当作 Web 服务器?骚操作,学废了~_前端_掘金安东尼_InfoQ写作社区