質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.39%
Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Q&A

解決済

1回答

2600閲覧

XMLHttpRequestを使ってローカルのファイルを読み込む

aaaa____

総合スコア29

Go

Go(golang)は、Googleで開発されたオープンソースのプログラミング言語です。

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

0グッド

0クリップ

投稿2022/11/11 12:52

編集2022/11/13 11:58

環境

・macOS Big Sur ver. 12.6 (21G115)
・MacBook Air M1, 2020 メモリ 8GB
・go1.18.3 darwin/arm64

現状

FileSystemAccessAPIについて調べていた際に,読み込みだけに限定するならば,manifest.jsonを作ってやることで,パスを使ってファイルを読み込むことができるというのが見つかったので,試してみています.
http://var.blog.jp/archives/84619582.html

作ったmanifest.json

json

1{ 2 "manifest_version": 3, 3 "name": "Read File", 4 "version": "1.0", 5 6 "permissions": ["file:///*"] 7}

index.htmlと同じ階層に置いてあります.

ファイル構成
├── ast ├── docs │ ├── build.wasm │ ├── index.html │ ├── manifest.json │ └── wasm_exec.js ├── evaluator ├── lexer ├── object ├── parser ├── repl ├── server ├── token ├── wasm │ ├── melody.txt │ └── webassembly.go ├── go.mod ├── go.sum └── main.go

困っていること

JavaScript

1const xhr = new XMLHttpRequest() 2xhr.open("GET", "/Users/usrname/Go/01/src/melody.txt") 3xhr.send() 4xhr.onload = () => console.log(xhr.responseText)

これを,Goに書き換えようとしているのですが.

Go

1 XMLHttpRequest = js.Global().Get("XMLHttpRequest") 2 xhr := XMLHttpRequest.New() 3 xhr.Call("open", "GET", "/Users/usrname/Go/01/src/melody.txt") 4 xhr.Call("send") 5 if (xhr.Call("onload") != `abort` && xhr.Call("onload") != `error`){ 6 code.Set("value", string(xhr.Get("responseText"))) 7 }

のように書き換えてビルドをしてみたところ,次のようなエラーが出てしまいました,

エラー内容->追記2へ
./webassembly.go:78:29: invalid operation: xhr.Call("onload") != `abort` (mismatched types js.Value and untyped string) ./webassembly.go:78:62: invalid operation: xhr.Call("onload") != `error` (mismatched types js.Value and untyped string) ./webassembly.go:79:29: cannot convert xhr.Get("responseText") (value of type js.Value) to type string

上2つの型の不一致の部分はstring(xhr.Call("onload")) != `abort` のようにすればエラーはなくなるのですが,これは一時凌ぎでエラーを掻い潜っているのではなくきちんと動作するのでしょうか.

また,3つめの箇所では,読み込んだファイルの内容を文字列としてcode.valueに入れたいのですが,stringで変換できない場合に他の対処法はあるのでしょうか.

追記

この状態で拡張機能をインストールすると,エラーが出てしまったので
https://www.extension.ninja/blog/post/solved-permission-is-unknown-or-url-pattern-is-malformed/
このサイトを参考にして

manifest.json

json

1{ 2 "manifest_version": 3, 3 "name": "Read File", 4 "version": "1.0", 5 6 "host_permissions": ["file:///*"] 7}

のようにしたところ,エラーがなくなりました.

また,Goで書き換えていた部分は次のようになりました.

Go

1 xhr := XMLHttpRequest.New() 2 xhr.Call("open", "GET", "file:///c:/Users/usrname/Go/01/src/melody.txt") 3 xhr.Call("send") 4 if (xhr.Get("onload").String() != `abort` && xhr.Call("onload").String() != `error`){ 5 code.Set("value", xhr.Get("responseText").String()) 6 }else{ 7 fmt.Println("fail of onload\n") 8 }

この状態でビルド,サーバーの実行を行ってブラウザに行ってボタンを押すと,次のようになりました.
onloadの部分は.Get()に変更したので関数のようにはしていないつもりなのですが,反映ができていない,もしくは,ここでのString()が不適切なので ==nullで通過させるようにしたほうがいいのかもしれません
!= nilにするとinvalid operation: xhr.Get("onload") != nil (mismatched types js.Value and untyped nil)のようにエラーが吐かれました.

Chromeを再起動させて更新は行いました.

https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest/load_event
上記のサイト周りを見ているのですが,イベントは扱いが異なるのでしょうか.

ですが,Not allowedとなる理由がわかっておりません.

Console

1wasm_exec.js:349 Not allowed to load local resource: file:///c:/Users/usrname/Go/01/src/melody.txt 2syscall/js.valueCall @ wasm_exec.js:349 3$syscall_js.valueCall @ build.wasm:0xe2bf9 4$syscall_js.Value.Call @ build.wasm:0xe0da4 5$main.main.func1 @ build.wasm:0x13616e 6$wasm_pc_f_loop @ build.wasm:0xd3719 7$wasm_export_resume @ build.wasm:0xd36fa 8_resume @ wasm_exec.js:538 9(anonymous) @ wasm_exec.js:549 10wasm_exec.js:22 panic: syscall/js: Value.Call: property onload is not a function, got null

追記2

Goで書き換えていた部分を以下のように修正しました.

Go

1 xhr := XMLHttpRequest.New() 2 xhr.Call("open", "GET", "file:///c:/Users/usrname/Go/01/src/melody.txt") 3 xhr.Call("send") 4 code.Set("value", xhr.Get("responseText").String())

このようにしたのちにビルドと実行を行うと
panic: syscall/js: Value.Call: property onload is not a function, got null
はなくなり,ブラウザのコンソール上で発生するエラーは次の一つだけとなりました.

Console

1wasm_exec.js:349 Not allowed to load local resource: file:///c:/Users/usrname/Go/01/src/melody.txt 2syscall/js.valueCall @ wasm_exec.js:349 3$syscall_js.valueCall @ build.wasm:0xe2bf9 4$syscall_js.Value.Call @ build.wasm:0xe0da4 5$main.main.func1 @ build.wasm:0x13616e 6$wasm_pc_f_loop @ build.wasm:0xd3719 7$wasm_export_resume @ build.wasm:0xd36fa 8_resume @ wasm_exec.js:538 9(anonymous) @ wasm_exec.js:549
調べてみたこと

番号リストリストhttps://qiita.com/smurakami/items/1764a80f40d33dce5243
このような記事が出て,軒並み
open -a /Applications/Google\ Chrome.app --args -allow-file-access-from-files index.html
をコンソールで打ち込めと出てきますが,そういった記事は2012や2013年の記事が多く,不安になってしまいます.
https://taremimi.hatenablog.jp/entry/2018/08/10/170035
新しめのでも2018などですね.
Chromeが開いてある状態でこのコマンドを実行しても意味ないですかね.

teratail上の同様の質問で
https://teratail.com/questions/28389
このようなものがあったのですが,見てみても解決できませんでした.

https://support.apple.com/ja-jp/guide/mac-help/mchld5a35146/mac
このサイトを頼りに「ファイルとフォルダ」を見に行きもしましたが,そもそもChromeはチェックボックスすらなく,Chromeを追加できる様子でもありませんでした.
https://apple.stackexchange.com/questions/385093/how-do-i-grant-access-in-files-and-folders-in-catalina-for-chrome
このサイトではChromeが見えているので何かして追加できるのかもしれません.

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2022/11/12 13:18

追記分でも xhr.onload = () => console.log(xhr.responseText) このJSの処理をGoに変換が正しくありません。
nobonobo

2022/11/12 13:19

やはり一旦Goのことは抜きにJSで目的を果たすということをやってみたほうが良いかもしれません。
aaaa____

2022/11/12 13:24

この部分はアロー関数式という認識で調べているのですが,合っていますでしょうか.
nobonobo

2022/11/12 13:33

はい。onloadはfunctionをセットすべきフィールドであって、onloadの値を見ることにあまり意味はありません。
aaaa____

2022/11/13 01:37 編集

アロー関数式の構文などと見比べて,()の左に項があって構文と違っていますし,名前つき関数でもないなと思っていたのですが.もしかしなくてもこれは意味合い的には, xhr.onload = ( () => console.log(xhr.responseText) ) となっていて,xhr.onloadにアロー関数式を入れているということになっていますでしょうか. とすると,別に今回のコードに入れる必要はなく, code.Set("value", xhr.Get("responseText").String()) をそのまま実行させれば良い,ということになりますか. この部分を編集した結果を追記2として書かせていただきます. 以下のサイトを見て,「リクエストが完了したときに、成功した場合 (load の後)、成功しなかった場合 (abort または error の後) のどちらでも発生します。 onloadend プロパティを通して利用することもできます。」から,条件分岐で書こうと判断していました. https://developer.mozilla.org/ja/docs/Web/API/XMLHttpRequest
nobonobo

2022/11/14 01:15

JSの非同期挙動について学びましょう。 xhr.send()した直後に「xhr.responseTextを参照」してもまだ格納されていないかと。 xhr.onloadに入れた関数は応答を受け取ったら呼ばれる関数を入れておくところで、 その関数が呼ばれたということは応答を受け取った後なので「xhr.responseTextを参照」することができます。 xhrは古い仕様ですし先行事例をみても書き方がいろいろあってわかりにくいと思います。 可能であればfetchを使いましょう(非同期の扱いが自然にできます)。
aaaa____

2022/11/14 11:09

なるほど,そのようになっているのですね. ご提案ありがとうございます,fetch周りや非同期挙動を調べてみることにします.
guest

回答1

0

ベストアンサー

これまでの質問のほとんどがいきなりGoで書こうとしてJS側のコンテキストを考慮できていないという問題を繰り返しています。

例えば「xhr.onload」はユーザーコードからコールするものではありません。functionを代入すべきフィールドです。

また、Goのsyscall/jsの実例をもっと見に行ったほうがいいと思います。js.Valueをstringにキャストはできません。js.Value.String()メソッドを使いましょう。

GoのWASMはJSのためのGoの記述を学びつつJSの知識も要求されるいばらの道なのですが突き進むというのであれば、
手順としては以下のものをお勧めします。

  1. まずJavascriptで目的の挙動を実現し動作をしっかり把握する
  2. 次にJSの記述をGoによる記述に置き換えていく

この二度手間を何度も繰り返したのち、慣れてくればいきなりGoで書けるようになっていきます。

また、Chrome拡張のつくり方はチュートリアルを済ませましたか?
これまでの延長でmanifest.jsonを書けばファイル読み込みができるというわけではありません。

追記への実装案

  1. Monkey処理系ごとWASMにした実装をロード&RUN。
  2. ブラウザでボタンを押したのを契機にローカルファイルを読み込む。
  3. ブラウザ側でファイルの内容に従ってMonkeyスクリプト処理する。

というフローでサーバーサイドではややこしいことはしたくないという考え方でしょうか。

だとすると、WASMファイルと一緒にスクリプトファイルも静的ファイルサーブするのが最もシンプルな気がします。

wasmファイル配信と同じフォルダに「sample.txt」を置いておき、双方静的ファイルサーブの対象とします。

go

1package main 2 3import ( 4 "log" 5 "syscall/js" 6) 7 8var ( 9 document = js.Global().Get("document") 10 fetch = js.Global().Get("fetch") 11) 12 13func load(fn string) string { 14 ch := make(chan string, 1) 15 success := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 16 log.Println("success") 17 var received js.Func 18 received = js.FuncOf(func(this js.Value, args []js.Value) interface{} { 19 defer received.Release() 20 log.Println("received") 21 ch <- args[0].String() 22 return nil 23 }) 24 args[0].Call("text").Call("then", received) 25 return nil 26 }) 27 defer success.Release() 28 failed := js.FuncOf(func(this js.Value, args []js.Value) interface{} { 29 log.Println("failed") 30 ch <- "" 31 return nil 32 }) 33 defer failed.Release() 34 go fetch.Invoke(fn).Call("then", success).Call("catch", failed) 35 return <-ch 36} 37 38func main() { 39 btn := document.Call("createElement", "button") 40 btn.Set("textContent", "run") 41 btn.Call("addEventListener", "click", js.FuncOf(func(js.Value, []js.Value) interface{} { 42 go func() { 43 log.Println(load("sample.txt")) 44 }() 45 return nil 46 })) 47 document.Get("body").Call("appendChild", btn) 48 select {} 49}

これであればsample.txtを書き換えて、ブラウザのボタンを押せば更新した内容をブラウザ側で読めるはずです。

投稿2022/11/11 15:54

編集2022/11/14 01:28
nobonobo

総合スコア3367

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

nobonobo

2022/11/11 16:02

js.Valueをstringにキャストはできません。js.Value.String()メソッドを使いましょう。
nobonobo

2022/11/11 16:13

たぶん、どういうファイルを用意してどういうときにファイルを更新し、どういうタイミングでそのファイルを読みたいのか実現したいデータのフローを明示してもらったほうがアドバイスしやすいかもしれません。
aaaa____

2022/11/12 01:20

JavaScriptの経験が足りず,まだしっかりとコンテキストを読み取りきれていないようですね,申し訳ございません. 今回も,参考サイトにJavaScriptでの記述があるのをいいことに何も考えずいきなりGoで書こうとしてしまっていました.焦らず,おっしゃられた二つの手順を意識しようと思います. Chrome拡張の作り方については https://qiita.com/n_yamadamadamada/items/4e469073486e11babddb このサイトを参考にしていたのですが,最後のところまできちんと目を通していませんでした. 申請が必要であり,なおかつ金銭のタスクも発生してしまうのですね. なるべく金銭のコストは発生させたくないので根本からやり方を見直すことになりそうです. Stringで取り出す方法についてはwebassembly.goの他の箇所でFloat型で取り出していたものと同様であるのに,染みついておらず,出てきていませんでした.確認不足でも申し訳ございません. フローとしましては, play(60, 0.3) のような命令が記述されたテキストファイル(.txt)を用意して,Monkey言語でplay文を実行するときにファイルを更新し,webassembly.goをビルドするとき,またはブラウザ上でボタンを押してシンセサイザーを鳴らす直前にそのファイルを読み込みたいと考えております.
nobonobo

2022/11/12 04:20

Chrome拡張を公開するためのハードルはありますが、開発モードでローカルインストールするならそのハードルは無視出来ます。
nobonobo

2022/11/14 01:34

老婆心ながらアドバイスしておきます。 Chrome拡張の役割に(ブラウジング体験を向上させるツールの実現など)沿ったなにかを作るのならまだしも、 ブラウザで禁止されていることをしたいためだけにChrome拡張に向かうのは労多く得るものは少ないと思います。 ブラウザで上で実現したいのであればブラウザの機能の中でやりくりするほうが得るものは多いと思います。
aaaa____

2022/11/14 11:39 編集

そうなのですね,みていた記事があまりにもさっぱりしていて,シンプルに書けるものだと思っていました. 覚えておきます. また,具体的なコードもありがとうございます. 考えていますのは,ブラウザに頼るのは音を鳴らす部分だけなので, Monkey言語の処理系全体をwasmするというよりは,play命令を受け取った時にその命令をテキストファイルに書き込み,そのテキストファイルの命令でブラウザで音を鳴らすというものです. 具体的には ``` % go run main.go Hello. This is The Monkey Programming language. Feel free to type in commands >> play(50, 0.2); write 19 bytes 50 >> play(60, 0.3); write 19 bytes 60 >> let i = 0; >> while(i< 3){play(51+i, 0.2); let i = i + 1;} write 19 bytes write 19 bytes write 19 bytes ``` のように命令していたらテキストファイルが ``` play(50, 0.200000) play(60, 0.300000) play(51, 0.200000) play(52, 0.200000) play(53, 0.200000) ``` となるので,これを以前のコードで評価させて音を鳴らそうというものです. ひとまず,私の理解が及ばず,この質問も長くなってしまったうえ,読み込みに関して具体的なコードをいただいたので,それを読んでみて自分のコードの中に組み込んでみて,また困った部分が出れば新しく質問させて頂こうと思っております.
nobonobo

2022/11/14 13:00

なるほど、CLI(コマンドラインインターフェースのツール)とブラウザの両方でMonkeyエンジンを動かすということですね。鳴らし始めるきっかけはブラウザ上のボタンですか? CLIでスクリプト処理ー>テキスト出力ののちブラウザのボタンでそれを読み取って再生するというフローであってますか?(以前のやり取りにあるようにブラウザのボタンを完全になくすことはできませんが)
aaaa____

2022/11/15 01:08

はい,動かし方はおっしゃる通りです,そのように表現するのですね,参考になります. きっかけはブラウザ上のボタンでと考えております. フローもその認識で問題ありません,ボタンが無くすことができないのは以前教えていただいた際に承知しておりますので,採用判断に含めた上で選ばさせていただいております.
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.39%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問