検索しても誰もそのプログラムを作っていないだって?なら自分が作るしかないな・・・という思考のユゲタです。
こうした「スキマ・プログラミング」がとても大好物な自分のため、他人からは理解されずらいプログラミングを日々構築しているのは言うまでもありません。
今回は、Nodejsで立ち上げたWebサーバーで、ファイルのデータアップロード処理を行いたくて、時間がかかったけど、成功をしたので、そのソースコードを公開しておきたいと思います。
最近ではReactなどが流行っているので、NodejsでのWebサーバーって、大手企業などでも使われていてメジャーになってきていると言ってもいいかもしれませんが、
こうした環境でのファイルアップロードって、multerライブラリを使う説明を書いている人がほとんどで、独自でパケットを読み込んでサーバーにファイルコピーをする方法がどこにも書かれていませんでした。
無駄なライブラリを使いたくないユゲタとしては、意地でも自分のコードで行いたかったので、その血と汗と涙とソースの結晶をご覧ください。
ソース
まず基本として、Nodejsのhttp機能を使って、サーバーを起動します。
このやり方はここでは説明しないので、ここから学びたい人は、
Google検索:nodejs httpサーバーなどをご覧ください。
その立ち上げたwebサーバーで、formタグのinputタグ、type属性が「file」のデータを送信した時の受け取りサーバーの処理コードになります。
formタグでのファイルアップロードについてよく分からない人は、
form ファイルアップロードこちらの検索結果を見てください。
module.exports = {
get_boundary_key : function(headers){
if(!headers){return null;}
const match = headers["content-type"].match(/^multipart\/form-data; boundary=(.+?)$/is);
if(!match || !match.length){return;}
return match[1];
},
get_data : async function(req){
if(!req || req.method !== "POST"){return {};}
const boundary_key = this.get_boundary_key(req.headers);
return new Promise(((resolve)=>{
let data = "";
req.on('data', (function(chunk) {
if(boundary_key){
let buffer = Buffer.from(chunk);
data += buffer.toString("binary");
}
else{
data += chunk
}
}).bind(this))
.on('end', (function(){
let new_data = {};
// 通常post情報
if(!boundary_key){
new_data = this.adjust_querys(data);
}
// form-dataアップロード
else if(boundary_key){
new_data = this.get_boundary_datas(boundary_key , data);
}
resolve(new_data);
}).bind(this))
}).bind(this));
},
adjust_querys : function(post_data){
let res = {};
const datas = post_data.split("&");
for(let data of datas){
const sp = data.split("=");
const key = decodeURIComponent(sp[0]);
const val = decodeURIComponent(sp.slice(1).join("="));
res[key] = val;
}
return res;
},
get_boundary_datas : function(key , post_data){
const arr = post_data.split(key);
let datas={};
for(let d of arr){
const names = d.match(/Content-Disposition: form-data; name="(.*?)"/i);
if(!names || !names[1]){
continue;
}
const name = decodeURIComponent(names[1]);
const filename = d.match(/filename="(.*?)"\r\n/i);
if(filename && filename[1]){
const contentType = d.match(/Content-Type: (.*?)\r\n/i);
const del_str = "\r\n"+ names[0] +"; "+ filename[0] + contentType[0] +"\r\n";
let data = d.replace(del_str , "");
const file_data = {
filename : filename[1],
contentType : contentType ? contentType[1] : "",
data : data
};
if(!datas[name]){
datas[name] = file_data;
}
else{
if(datas[name].constructor !== Array){
datas[name] = [datas[name]];
}
datas[name].push(file_data);
}
}
else{
const del_str = "\r\n"+ names[0] +"\r\n\r\n";
let data = d.replace(del_str , "");
data = data.replace(/\r\n--$/g , "");
const v = decodeURIComponent(data);
datas[name] = v;
}
}
return datas;
}
};
使い方
まず、上記のソースコードを、「request2postDatas.js」として保存します。
ファイルをrequireしておいて、httpサーバーで受け取ったrequestを下記のように送ると、データ込みのPOSTデータを受け取る事ができます。
const http = require("http");
const request2postDatas = require("request2postDatas.js");
const server = http.createServer();
server.on('request', async function(req , res){
const post = await request2postDatas(req);
});
server.listen(8000 , "localhost");
※このファイルは、httpサーバー立ち上げサンプルにrequest2postDatasを実行するサンプルなので、このまま実行をしても正常に機能しません。
ポイントとしては、request2postは、asyncフラグを立てて、同期処理にて情報を受け取ってください。
request2postDatasにrequest(req)情報を送る事で、そこに含まれているheaders情報内の、boundary情報を取得して、postデータのチャンクデータを受け取るようにしています。
受け取ったPOSTデータ仕様
受け取ったpostデータ内の構造は次のようになっています。
const post = request2post(requiest);
> post
{
"name_1" : "a", // input type="text" name="name_1" value="a">
"name_2" : "b", // input type="text" name="name_2" value="b">
"file_1" : { // input type="file" name="file_1">
"filename" : "アップロードしたファイル名",
"contentType" : "ファイルのcontent-type",
"data" : バイナリデータ
}
}
一応、テキストなどのpost情報も同時に取得できるようにしているので、便利に使いやすくなっていると思います。
そして、受け取った画像データなどのバイナリデータは、fsライブラリで次のようにすると、ファイルとして保存をすることができます。
const fs = require("fs");
fs.writeFileSync("保存するファイル名" , post["file_1"].data , "binary");
これで、無事に、ファイルアップロード処理が完了できたわけです。
いや〜、3日かかりました。この処理を作るのに、なかなかバイナリ結合がうまくできなくて、それができた時の喜びこそが、プログラミングの楽しみだと感じたプログラミングでした。
この記事がどなたかの参考になる事を願っています。
0 件のコメント:
コメントを投稿