Symfoware

Symfowareについての考察blog

ブラウザ(JavaScript)で複数のファイルを一括ダウンロードさせる

例えば1回ボタンを押したら、3つの画像を一気にダウンロードさせたい。


aタグのclick



こちらが参考になりました。
【JavaScript】各ブラウザでダウンロード処理を実装する(Chrome, Firefox, IE, MS Edge, Safari)
ファイルをダウンロードさせるには?[JavaScript]


html側


  1. <!doctype html>
  2. <html lang="ja">
  3. <head>
  4.     <meta charset="utf-8">
  5.     <title>Download Sample</title>
  6. </head>
  7. <body>
  8. <script type='text/javascript'>
  9. function multi_download() {
  10.     handleDownload("001.jpg");
  11.     handleDownload("002.jpg");
  12.     handleDownload("003.jpg");
  13. }
  14. function handleDownload(name) {
  15.     
  16.     // IE
  17.     if (window.navigator.msSaveBlob) {
  18.         var xhr = new XMLHttpRequest();
  19.         xhr.open("GET", "cgi-bin/sample.py?f="+name, true);
  20.         xhr.responseType = "blob";
  21.         xhr.onload = function (e) {
  22.             var blob = xhr.response;
  23.             window.navigator.msSaveBlob(blob, name);
  24.         }
  25.         xhr.send();
  26.         return;
  27.     }
  28.     
  29.     // chrome,firefox
  30.     var a = document.createElement('a');
  31.     a.download = name;
  32.     a.href = "cgi-bin/sample.py?f="+name;
  33.     
  34.     document.body.appendChild(a);
  35.     a.click();
  36.     document.body.removeChild(a);
  37.     
  38.     
  39. }
  40. </script>
  41.     <input type="button" value="ダウンロード" onclick="multi_download()">
  42. </body>
  43. </html>




aタグに画像ダウンロード用のスクリプトを指定します。
直接画像を指定しても良いのですが、
「Content-Type: application/force-download」
としたいため、一旦スクリプトを経由して画像ファイルのダウンロードを実行しています。

IEでもchromeやfirefoxで使用しているaタグを利用する手が使えるのですが、
a.target = "_blank"
を指定しないとダウンロードしてくれません。

ダウンロードする画像数分タブが表示されては消える挙動となるため、
msSaveBlobで保存しています。



cgiスクリプトは、ヘッダーをつけて画像を読み込み返すだけ。


  1. #!/usr/bin/env python
  2. # -*- coding:utf-8 -*-
  3. import sys
  4. import cgi
  5. import cgitb
  6. cgitb.enable(display=0, logdir='./')
  7. # パラメーターを取得
  8. form = cgi.FieldStorage()
  9. file_name = form['f'].value
  10. print('Content-Type: application/force-download;');
  11. print('Content-disposition: attachment; filename="' + file_name + '";');
  12. print('X-Download-Options: noopen\r\n')
  13. with open('image/' + file_name, 'rb') as f:
  14.     sys.stdout.write(f.read())






chromeでの動作



ボタンをクリックすると、初回のみ「複数ファイルのダウンロード」のダイアログが表示されます。

744_01.png

「許可」を選択すると、複数ファイルのダウンロードが実行されます。

「許可」を選択した場合、設定の[プライバシー] - [コンテンツの設定]の項目
[自動ダウンロード]の例外の管理に追加されます。

744_02.png




firefoxでの動作



ファイルに対しての操作を確認されます。
「ファイルを保存する」を選択し、「今後この種類のファイルは同様に処理する」としておけば、
複数ファイルのダウンロードが可能となります。

744_03.png


「設定」のプログラムで管理されています。

744_04.png




IE



IEの場合は厄介で、ファイル数分確認のダイアログが表示されます。

744_05.png




【参考URL】

【JavaScript】各ブラウザでダウンロード処理を実装する(Chrome, Firefox, IE, MS Edge, Safari)
ファイルをダウンロードさせるには?[JavaScript]
JavaScriptでファイルダウンロード処理を実現する
Web アプリケーションからファイルを扱う
PDFなどのファイルを強制的にダウンロードさせる3つ方法の比較
pythonでダウンロードCGIにハマった件の解決

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/04/16(日) 19:12:16|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

Riot ajaxなど非同期通信後の再描画

ajax通信でデータを取得。
Riotで画面を再描画してみました。
Riot ajaxで取得したjsonデータをテーブルに表示する(axios使用)

よくドキュメントを読んでみると、updateは引数を取ります。
API · カスタムタグ


変更前のソース



変更前はこんなソースでした。


  1. <!doctype html>
  2. <html lang="ja">
  3.     
  4. <html>
  5. <head>
  6.     <meta charset="UTF-8">
  7.     <title>Riot + axios</title>
  8.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  9. </head>
  10. <body>
  11.     <!-- ここにタグの内容を反映 -->
  12.     <my-tag></my-tag>
  13. </body>
  14. <!-- ここからRiotプログラム -->
  15. <!-- カスタムタグ -->
  16. <script type="riot/tag">
  17. <my-tag>
  18. <h3>ボタンクリックでデータ取得</h3>
  19. <div><input type="button" value="追加" onclick={ add }></div>
  20. <table border="1">
  21.     <thead>
  22.      <tr>
  23.         <td>id</td><td>value</td>
  24.      </tr>
  25.     </thead>
  26.     <tbody>
  27.     <tr each={ items }>
  28.         <td>{ id }</td>
  29.         <td>{ value }</td>
  30.     </tr>
  31.     </tbody>
  32. </table>
  33. var self = this;
  34. self.items = []
  35. add(event) {
  36.      axios.get('/api')
  37.         .then(function (response) {
  38.             self.items = response.data
  39.             self.update()
  40.         })
  41.         .catch(function (error) {
  42.             console.log(error)
  43.         });
  44. }
  45. </my-tag>
  46. </script>
  47. <!-- マウント -->
  48. <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.1/axios.min.js"></script>
  49. <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/3.4.0/riot%2Bcompiler.min.js"></script>
  50. <script>
  51. riot.mount('my-tag')
  52. </script>
  53.     
  54. </html>




self.update()で変更後のデータを渡すようにしてやります。


  1. <!doctype html>
  2. <html lang="ja">
  3.     
  4. <html>
  5. <head>
  6.     <meta charset="UTF-8">
  7.     <title>Riot + axios</title>
  8.     <meta http-equiv="X-UA-Compatible" content="IE=edge">
  9. </head>
  10. <body>
  11.     <!-- ここにタグの内容を反映 -->
  12.     <my-tag></my-tag>
  13. </body>
  14. <!-- ここからRiotプログラム -->
  15. <!-- カスタムタグ -->
  16. <script type="riot/tag">
  17. <my-tag>
  18. <h3>ボタンクリックでデータ取得</h3>
  19. <div><input type="button" value="追加" onclick={ add }></div>
  20. <table border="1">
  21.     <thead>
  22.      <tr>
  23.         <td>id</td><td>value</td>
  24.      </tr>
  25.     </thead>
  26.     <tbody>
  27.     <tr each={ items }>
  28.         <td>{ id }</td>
  29.         <td>{ value }</td>
  30.     </tr>
  31.     </tbody>
  32. </table>
  33. var self = this;
  34. self.items = []
  35. add(event) {
  36.      axios.get('/api')
  37.         .then(function (response) {
  38.             //self.items = response.data
  39.             //self.update()
  40.             
  41.             // 変更後のデータを設定
  42.             self.update({items : response.data})
  43.         })
  44.         .catch(function (error) {
  45.             console.log(error)
  46.         });
  47. }
  48. </my-tag>
  49. </script>
  50. <!-- マウント -->
  51. <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.1/axios.min.js"></script>
  52. <script src="https://cdnjs.cloudflare.com/ajax/libs/riot/3.4.0/riot%2Bcompiler.min.js"></script>
  53. <script>
  54. riot.mount('my-tag')
  55. </script>
  56.     
  57. </html>




動作は変わりません。
こちらのほうがすっきりしていて良いですね。


テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/04/09(日) 23:28:44|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

java 8 jjsスクリプト sshトンネルを経由し、MariaDBのデータを取得する

java8から搭載されたjjsスクリプトを試しています。
OpenJDK 8 jjsコマンドでJavaScriptを実行する


SSHトンネルを経由してデータベースに接続できることを知りました。
HeidiSQL SSH tunnel経由でMySQL(MariaDB)に接続する

jjsスクリプトでデータベースに接続する方法もわかりました。
java 8 jjsスクリプト JDBCドライバを使用してデータベースに接続する


これらを踏まえ、jjsスクリプトを使用し、sshトンネル経由でMariaDBのデータを取得してみます。
こちらを参考にさせていただきました。
JDBCでSSHトンネルを経由してMySQLに接続する方法



jsch



javaでssh接続を行うには、jschを使用するのが良さそうです。
http://www.jcraft.com/jsch/

jsch-0.1.54.jarをダウンロードしました。

726_01.png


jsch-0.1.54.jarをこれから作成するjjsスクリプトと同じ階層にコピーしておきます。





サンプル




ホスト:192.168.1.103

sshユーザー:sshuser
sshパスワード:sshuser123
sshポート:22

データベースユーザー:root
データベースパスワード:P@ssw0rd
データベース名:sample
テーブル:test




上記の条件で接続してみます。

・sample.js


  1. var jsch = new com.jcraft.jsch.JSch()
  2. // ユーザー、サーバー名、ポートを指定
  3. var session = jsch.getSession('sshuser', '192.168.1.103', 22)
  4. // ssh接続のパスワードを指定
  5. session.setPassword('sshuser123')
  6. var config = new java.util.Properties()
  7. config.put("StrictHostKeyChecking", "no")
  8. session.setConfig(config)
  9. session.connect()
  10. // 3307 <-> 3036でsshトンネルを生成
  11. var remoteHost = "127.0.0.1"
  12. var localPort = 3307
  13. var remotePort = 3306
  14. session.setPortForwardingL(localPort, remoteHost, remotePort)
  15. // データベース接続
  16. var prop = new java.util.Properties()
  17. prop.setProperty('user', 'root')
  18. prop.setProperty('password', 'P@ssw0rd')
  19. // ポート番号の指定は「3307」であることに注意
  20. var url = "jdbc:mariadb://localhost:3307/sample"
  21. var driver = new org.mariadb.jdbc.Driver()
  22. var con = driver.connect(url, prop)
  23. var stmt = con.createStatement()
  24. var rs = stmt.executeQuery('select * from test')
  25. while(rs.next()){
  26.     print(rs.getInt('id') + ':' + rs.getString('item'))
  27. }
  28. session.disconnect()
  29. print('ok')





JDBCドライバーとjschのクラスパスを指定して実行。


$ jjs -cp mariadb-java-client-1.5.6.jar:jsch-0.1.54.jar sample.js
1:test1
2:test2
3:test3
4:test4
5:test5
ok




ちゃんとsshトンネル経由でデータベースに接続できたようです。



【参考URL】

JDBCでSSHトンネルを経由してMySQLに接続する方法

HeidiSQL SSH tunnel経由でMySQL(MariaDB)に接続する

java 8 jjsスクリプト JDBCドライバを使用してデータベースに接続する

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/01/15(日) 22:01:31|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

java 8 jjsスクリプト http接続とjsonデータのデコード

java8から搭載されたjjsスクリプトを試しています。
OpenJDK 8 jjsコマンドでJavaScriptを実行する

http接続を実行し、json形式のデータを取得。
取得したjsonデータをパースし、表示してみます。


サーバー側のソース



phpで適当にJSONデータを返却するプログラムを準備しておきました。

・sample.php


  1. <?php
  2. $t = [
  3.     'a' => '日本語',
  4.     'b' => 'テストです',
  5.     'c' => [
  6.         'c-1' => 'テスト1',
  7.         'c-2' => 'テスト2',
  8.         'c-3' => 'テスト3',
  9.     ],
  10. ];
  11. echo json_encode($t);




アクセスすると、こんな表示になります。

725_01.png





jjsスクリプト




java.net.URLを使用してサーバー側のリソースを取得。
JavaでのHttpClientサンプル

JSON.parseで取得したjsonな文字列をデコードします。
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON



・sample.js


  1. // サーバーからデータを取得
  2. var url = new java.net.URL('http://192.168.1.103/sample.php')
  3. var con = url.openConnection()
  4. con.setRequestMethod("GET")
  5. // 取得したデータを文字列として取得
  6. var isr = new java.io.InputStreamReader(con.getInputStream(), java.nio.charset.StandardCharsets.UTF_8)
  7. var reader = new java.io.BufferedReader(isr)
  8. var json_value = ''
  9. while ((line = reader.readLine()) != null) {
  10.     json_value += line
  11. }
  12. // 文字列からオブジェクトに変換し表示
  13. var json = JSON.parse(json_value)
  14. print(json['a'])
  15. print(json['b'])
  16. print(json['c'])
  17. print(json['c']['c-1'])
  18. print(json['c']['c-2'])
  19. print(json['c']['c-3'])




ちゃんとJSON形式の文字列が連想配列に変換できたようです。


$ jjs sample.js
日本語
テストです
[object Object]
テスト1
テスト2
テスト3





【参考URL】

JavaでのHttpClientサンプル

https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/01/15(日) 21:27:59|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集

java 8 jjsスクリプト JDBCドライバを使用してデータベースに接続する

java8から搭載されたjjsスクリプトを試しています。
OpenJDK 8 jjsコマンドでJavaScriptを実行する

JDBCドライバをロードし、データベースに接続。
データの取得が行えるか試してみます。


JDBCドライバの入手



MariaDBへの接続を試してみます。

まず、こちらを参考にJDBCドライバを入手しました。
MariaDB 10にJDBCドライバ(MariaDB Connector/J 1.1.9)で接続する

ダウンロードURLはこちら。


Download MariaDB Connector/Jをクリック

724_01.png


Download 1.5.6 Stable Now!をクリック

724_02.png


MariaDB Connector/J .jar をクリック

724_03.png


mariadb-java-client-1.5.6.jarをダウンロードしました。

724_04.png


ダウンロードしたjarファイルは、これから作成するスクリプトファイルと
同じ階層に保存しておきます。




サンプルプログラム




サーバー:192.168.1.103
ユーザー:admin
パスワード:P@ssw0rd
データベース:sample
テーブル:test



上記条件のデータベースに接続するサンプルを書いてみます。


・sample.js


  1. var url = "jdbc:mariadb://192.168.1.103:3306/sample"
  2. var con = java.sql.DriverManager.getConnection(url, 'admin', 'P@ssw0rd')
  3. var stmt = con.createStatement()
  4. var rs = stmt.executeQuery('select * from test')
  5. while(rs.next()){
  6.     print(rs.getInt('id') + ':' + rs.getString('item'))
  7. }
  8. print('ok')




jjsの引数を見ると「-cp」でクラスパスを指定できる模様。
ダウンロードした「mariadb-java-client-1.5.6.jar」を指定して実行するとエラーになります。


$ jjs -cp mariadb-java-client-1.5.6.jar sample.js
Exception in thread "main" java.lang.RuntimeException: java.sql.SQLException:
No suitable driver found for jdbc:mariadb://192.168.1.103:3306/sample




JDBCドライバは使用できないのかな?と悩んでいたところ、この記事を見つけました。
how to specify the CLASSPATH for for jjs/Nashorn javascript?


「-J-Djava.class.path=」でドライバのjarファイルを指定しろとのこと。
-Jオプションは、Java仮想マシン(JVM)に対するオプションのようです。
https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/javac.html


オプションを変更して、再度実行。


$ jjs -J-Djava.class.path=mariadb-java-client-1.5.6.jar sample.js
1:test1
2:test2
3:test3
4:test4
5:test5
ok



ちゃんとデータベースに接続し、データの取得が行えました。






もうひとつのサンプル



せっかく-cpオプションがあるので、この指定だけでデータベースに接続できないか考えてみます。

以前、JDBCドライバについて調べた時のことが役に立ちました。
JDBC over Thriftを作成する その1(JDBCドライバの作り方)


DriverManagerを使用せず、直接対象のJDBCドライバーを生成。
コネクションを取得してやります。


  1. var prop = new java.util.Properties()
  2. prop.setProperty('user', 'admin')
  3. prop.setProperty('password', 'P@ssw0rd')
  4. var url = "jdbc:mariadb://192.168.1.103:3306/sample"
  5. var driver = new org.mariadb.jdbc.Driver()
  6. var con = driver.connect(url, prop)
  7. var stmt = con.createStatement()
  8. var rs = stmt.executeQuery('select * from test')
  9. while(rs.next()){
  10.     print(rs.getInt('id') + ':' + rs.getString('item'))
  11. }
  12. print('ok')




強引ですが、この書き方であれば-cpオプションにJDBCドライバのjarファイルを
指定することで実行可能となります。


$ jjs -cp mariadb-java-client-1.5.6.jar sample.js
1:test1
2:test2
3:test3
4:test4
5:test5
ok





【参考URL】

MariaDB 10にJDBCドライバ(MariaDB Connector/J 1.1.9)で接続する

how to specify the CLASSPATH for for jjs/Nashorn javascript?

https://docs.oracle.com/javase/jp/8/docs/technotes/tools/windows/javac.html

JDBC over Thriftを作成する その1(JDBCドライバの作り方)

テーマ:プログラミング - ジャンル:コンピュータ

  1. 2017/01/15(日) 21:14:41|
  2. Java
  3. | トラックバック:0
  4. | コメント:0
  5. | 編集
次のページ