2021年6月4日金曜日

denoでローカルエリアにあるテキストファイルのPathをブラウザから指定して中身を読み込むサンプル

ブラウザに表示されたinputボックスにテキストファイルのPathを入力してボタンを押すと、内容がテキストエリアエレメントに表示される、というサンプルが以下。
// 動作確認したい機能だけをこのファイルにまとめる。
import {
WebSocket,
isWebSocketCloseEvent,
} from "https://deno.land/std/ws/mod.ts";
export async function wsHandler(ws: WebSocket): Promise<void> {
const id = ++clientId;
clients.set(id, ws);
console.log(`[${id}]さんが入室しました`);
for await (const msg of ws) {
if (typeof msg === "string") {
msg解釈(msg)
continue
} else if (isWebSocketCloseEvent(msg)) {
clients.delete(id);
dispatch(`[${id}]さんが退室しました`);
break;
}
}
}
// 以下はexportしない(import先からコールできない)関数など。
async function msg解釈(msg:string): Promise<void> {
console.log('msg解釈['+msg+']')
const text = await テキストファイルを読み込む(msg)
dispatch(text)
}
async function テキストファイルを読み込む(path:string): Promise<string> {
const file = await Deno.open(path, {read: true})
const data = await await Deno.readAll(file)
Deno.close(file.rid)
const text = new TextDecoder().decode(data)
return text
}
const clients = new Map<number, WebSocket>();
let clientId = 0;
function dispatch(msg: string): void {
console.log('dispatch:'+msg)
for (const key of clients.keys()) {
let client = clients.get(key)
if(client==undefined){
clients.delete(key)
continue
}
try{ client.send(msg) }
catch(e){
// メッセージの送信に失敗した相手は切断済みと判断する。
clients.delete(key)
console.log('send err. key['+key+'] msg['+msg+']')
}
}
}
view raw core.ts hosted with ❤ by GitHub
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<title>test read</title>
<link rel="stylesheet" type="text/css" href="./css.css">
</head>
<body>
Path:<input id=inpPath style="width:150px">
<button id=btn>読込</button>
<textarea id=ta style="width:250px;height:100px"></textarea>
</body>
<script>
let ws
function ws接続(){
if(ws){ ws.close() }
ws = new WebSocket(`ws://${location.host}/ws`)
ws.addEventListener("open", () => {
console.log('webSocket:open')
});
ws.addEventListener("message", ({data}) => {
denoから受信(data)
});
ws.addEventListener("close", () => {
console.log('webSocket:close')
});
}
btn.onclick = function(){
ws.send(inpPath.value)
}
function denoから受信(msg) {
ta.value = msg
}
ws接続()
</script>
</html>
view raw index.html hosted with ❤ by GitHub
// --allow-run --allow-net --allow-read
import { listenAndServe } from "https://deno.land/std/http/server.ts";
import * as path from "https://deno.land/std@0.97.0/path/mod.ts"; // pathResolver
import {
acceptWebSocket,
acceptable,
} from "https://deno.land/std/ws/mod.ts";
import { wsHandler } from './core.ts'
function 起動(): void{
for(let port=49152; port<=65535 ;port++){
try{
const server = listenAndServe({ port: port }, async (req)=> { リクエスト処理(req) });
console.log(`HTTP webserver running. Access it at: http://localhost:` + port + `/`);
}
catch(e){continue}
ブラウザ起動(port)
break
}
}
function ブラウザ起動(port: number){
Deno.run({cmd:['cmd', '/C start http://localhost:'+port]})
}
async function リクエスト処理(request: any){
if(request.method != 'GET'){return}
console.log('url:[' + request.url + ']')
let url = request.url
if(url=='/ws'){return webSocket接続(request)}
if(url=='/'){ url = './index.html' }
if(url.indexOf('/')==0){ url = '.' + url }
url = resolve(url)
const ファイルの有無 = await fileExists(url)
if(!ファイルの有無){
return request.respond({ status: 404 })
}
const file = await Deno.open(url);
request.respond({
status: 200,
headers: new Headers({"content-type": 拡張子2contentType(url)}),
body: file
});
}
function 拡張子2contentType(url: string):string{
if(url.match(/\.htm(l?)$/)){return 'text/html'}
if(url.match(/\.css$/)){return 'text/css'}
return ''
}
async function fileExists(filePath: string): Promise<boolean> {
try {
await Deno.lstat(filePath);
return true;
} catch (err) {
if (err instanceof Deno.errors.NotFound) {
return false;
}
throw err;
}
}
function webSocket接続(request: any):void{
console.log('webSocket接続')
if(!acceptable(request)){return console.log('webSocket接続 not acceptable')}
acceptWebSocket({
conn: request.conn,
bufReader: request.r,
bufWriter: request.w,
headers: request.headers,
}).then(wsHandler);
}
function pathResolver(meta: ImportMeta): (p: string) => string {
return (p) => path.fromFileUrl(new URL(p, meta.url))
}
const resolve = pathResolver(import.meta);
起動()
view raw main.ts hosted with ❤ by GitHub
実行結果



上記はサンプルを起動している最中はネットワーク接続された他のデバイスなどからファイルを覗き見されるリスクがあります。
動作させる場合はセキュリティにご注意ください。

0 件のコメント:

コメントを投稿