w-converhp 2.0.14 → 2.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -26,6 +26,11 @@ import mergeFiles from './mergeFiles.wk.umd.js'
26
26
  // import mergeFiles from './mergeFiles.mjs'
27
27
 
28
28
 
29
+ //回傳前端stream時(POST或GET皆可), 前端會須等stream傳完才能判斷是否為大檔或錯誤訊息, 此會導致若回傳超大檔, 會需要對超大檔進行解析會有記憶體上限問題, 故需要通過header提供基本成功或失敗訊息, 讓前端能進行解析判斷
30
+ //回傳前端(nodejs)時, 針對超大檔, 只能用POST並用stream回傳
31
+ //回傳前端(browser)時, 針對超大檔, 可用POST並用stream回傳但還要處理進度條, 若要交由瀏覽器下載器處理, 只能用GET並用stream回傳, 且前端只能用window.location.href或a.href+a.click()下載
32
+
33
+
29
34
  /**
30
35
  * 建立Hapi伺服器
31
36
  *
@@ -36,6 +41,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
36
41
  * @param {String} [opt.pathStaticFiles='dist'] 輸入當useInert=true時提供瀏覽資料夾字串,預設'dist'
37
42
  * @param {String} [opt.apiName='api'] 輸入API名稱字串,預設'api'
38
43
  * @param {String} [opt.pathUploadTemp='./uploadTemp'] 輸入暫時存放切片上傳檔案資料夾字串,預設'./uploadTemp'
44
+ * @param {String} [opt.tokenType='Bearer'] 輸入token類型字串,預設'Bearer'
39
45
  * @param {Function} [opt.verifyConn=()=>{return true}] 輸入呼叫API時檢測函數,預設()=>{return true}
40
46
  * @param {Array} [opt.corsOrigins=['*']] 輸入允許跨域網域陣列,若給予['*']代表允許全部,預設['*']
41
47
  * @param {Integer} [opt.delayForBasic=0] 輸入基本API用延遲響應時間,單位ms,預設0
@@ -44,14 +50,24 @@ import mergeFiles from './mergeFiles.wk.umd.js'
44
50
  * @returns {Object} 回傳事件物件,可監聽事件execute、upload、handler
45
51
  * @example
46
52
  *
53
+ * import fs from 'fs'
47
54
  * import _ from 'lodash-es'
48
- * import WConverhpServer from 'w-converhp/dist/w-converhp-server.umd.js'
55
+ * import w from 'wsemi'
56
+ * import WConverhpServer from './src/WConverhpServer.mjs'
57
+ *
58
+ * let ms = []
49
59
  *
50
60
  * let opt = {
51
61
  * port: 8080,
52
62
  * apiName: 'api',
53
63
  * pathStaticFiles: '.', //要存取專案資料夾下web.html, 故不能給dist
54
- * verifyConn: () => {
64
+ * verifyConn: async ({ apiType, authorization, headers, query }) => {
65
+ * console.log('verifyConn', `apiType[${apiType}]`, `authorization[${authorization}]`)
66
+ * let token = w.strdelleft(authorization, 7) //刪除Bearer
67
+ * if (!w.isestr(token)) {
68
+ * return false
69
+ * }
70
+ * // await w.delay(3000)
55
71
  * return true
56
72
  * },
57
73
  * }
@@ -69,6 +85,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
69
85
  *
70
86
  * if (_.get(input, 'p.d.u8a', null)) {
71
87
  * console.log('input.p.d.u8a', input.p.d.u8a)
88
+ * ms.push({ 'input.p.d.u8a': input.p.d.u8a })
72
89
  * }
73
90
  *
74
91
  * let r = {
@@ -76,13 +93,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
76
93
  * _data: [11, 22.22, 'abc', { x: '21', y: 65.43, z: 'test中文' }],
77
94
  * _bin: {
78
95
  * name: 'zdata.b2',
79
- * u8a: new Uint8Array([66, 97, 115]),
80
- * // name: '100mb.7z',
81
- * // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\100mb.7z')),
82
- * // name: '500mb.7z',
83
- * // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\500mb.7z')),
84
- * // name: '1000mb.7z',
85
- * // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\1000mb.7z')),
96
+ * u8a: new Uint8Array([52, 66, 97, 115]),
86
97
  * },
87
98
  * }
88
99
  *
@@ -105,6 +116,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
105
116
  * console.log(`Server[port:${opt.port}]: upload`, input)
106
117
  *
107
118
  * try {
119
+ * ms.push({ 'receive and return': input })
108
120
  * let output = input
109
121
  * pm.resolve(output)
110
122
  * }
@@ -117,29 +129,32 @@ import mergeFiles from './mergeFiles.wk.umd.js'
117
129
  * wo.on('download', (input, pm) => {
118
130
  * console.log(`Server[port:${opt.port}]: download`, input)
119
131
  *
132
+ * let streamRead = null
120
133
  * try {
121
134
  * ms.push({ 'download': input })
122
135
  *
123
136
  * //fp
124
137
  * let fp = `./test/1mb.7z`
125
138
  *
126
- * //streamRead
127
- * let streamRead = fs.createReadStream(fp)
128
- *
129
- * //fileName
130
- * let fileName = `1mb.7z`
139
+ * //check, 檔案存在才往下
131
140
  *
132
141
  * //fileSize
133
142
  * let stats = fs.statSync(fp)
134
143
  * let fileSize = stats.size
135
144
  *
145
+ * //streamRead
146
+ * streamRead = fs.createReadStream(fp)
147
+ *
148
+ * //filename
149
+ * let filename = `1mb中文.7z` //測試支援中文
150
+ *
136
151
  * //fileType
137
152
  * let fileType = 'application/x-7z-compressed'
138
153
  *
139
154
  * //output
140
155
  * let output = {
141
156
  * streamRead,
142
- * fileName,
157
+ * filename,
143
158
  * fileSize,
144
159
  * fileType,
145
160
  * }
@@ -148,14 +163,27 @@ import mergeFiles from './mergeFiles.wk.umd.js'
148
163
  * }
149
164
  * catch (err) {
150
165
  * console.log('download error', err)
166
+ * // try {
167
+ * // streamRead.destroy() //若fs.createReadStream早於fs.statSync執行, 但fs.statSync發生錯誤時, stream得要destroy
168
+ * // }
169
+ * // catch (err) {}
151
170
  * pm.reject('download error')
152
171
  * }
153
172
  *
154
173
  * })
174
+ * wo.on('error', (err) => {
175
+ * console.log(`Server[port:${opt.port}]: error`, err)
176
+ * })
155
177
  * wo.on('handler', (data) => {
156
178
  * // console.log(`Server[port:${opt.port}]: handler`, data)
157
179
  * })
158
180
  *
181
+ * setTimeout(() => {
182
+ * console.log('ms', ms)
183
+ * // console.log('ms', JSON.stringify(ms))
184
+ * wo.stop()
185
+ * }, 3000)
186
+ *
159
187
  */
160
188
  function WConverhpServer(opt = {}) {
161
189
 
@@ -183,12 +211,6 @@ function WConverhpServer(opt = {}) {
183
211
  apiName = 'api'
184
212
  }
185
213
 
186
- //apiUploadSliceName
187
- let apiUploadSliceName = `${apiName}slc`
188
-
189
- //apiDownloadName
190
- let apiDownloadName = `${apiName}dw`
191
-
192
214
  //pathUploadTemp
193
215
  let pathUploadTemp = get(opt, 'pathUploadTemp')
194
216
  if (!isestr(pathUploadTemp)) {
@@ -198,6 +220,12 @@ function WConverhpServer(opt = {}) {
198
220
  fsCreateFolder(pathUploadTemp)
199
221
  }
200
222
 
223
+ //tokenType
224
+ let tokenType = get(opt, 'tokenType')
225
+ if (!isestr(tokenType)) {
226
+ tokenType = 'Bearer'
227
+ }
228
+
201
229
  //verifyConn
202
230
  let verifyConn = get(opt, 'verifyConn')
203
231
  if (!isfun(verifyConn)) {
@@ -558,7 +586,7 @@ function WConverhpServer(opt = {}) {
558
586
 
559
587
  //apiUploadSlice
560
588
  let apiUploadSlice = {
561
- path: `/${apiUploadSliceName}`,
589
+ path: `/${apiName}slc`,
562
590
  method: 'POST',
563
591
  options: {
564
592
  payload: {
@@ -700,7 +728,7 @@ function WConverhpServer(opt = {}) {
700
728
 
701
729
  //apiUploadSliceMerge
702
730
  let apiUploadSliceMerge = {
703
- path: `/${apiUploadSliceName}m`,
731
+ path: `/${apiName}slcm`,
704
732
  method: 'POST',
705
733
  options: {
706
734
  payload: {
@@ -708,7 +736,7 @@ function WConverhpServer(opt = {}) {
708
736
  maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
709
737
  timeout: false, //避免請求未完成時中斷
710
738
  // output: 'stream',
711
- parse: true, //前端送obj過來
739
+ parse: true, //前端送obj過來須自動解析
712
740
  },
713
741
  timeout: {
714
742
  server: false, //關閉伺服器超時
@@ -818,9 +846,263 @@ function WConverhpServer(opt = {}) {
818
846
  },
819
847
  }
820
848
 
849
+ //apiDownloadGetFilename
850
+ let apiDownloadGetFilename = {
851
+ path: `/${apiName}dwgfn`,
852
+ method: 'POST',
853
+ options: {
854
+ payload: {
855
+ maxBytes: 1024 * 1024 * 1024 * 1024, //預設為1mb, 調整至1tb, 也就是給予3次方
856
+ maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
857
+ timeout: false, //避免請求未完成時中斷
858
+ // output: 'stream',
859
+ parse: true, //前端送obj過來須自動解析
860
+ },
861
+ timeout: {
862
+ server: false, //關閉伺服器超時
863
+ socket: false, //關閉socket超時
864
+ },
865
+ },
866
+ handler: async function (req, res) {
867
+ // console.log(req, res)
868
+ // console.log('payload', req.payload)
869
+
870
+ //headers
871
+ let headers = get(req, 'headers')
872
+ headers = iseobj(headers) ? headers : ''
873
+ // console.log('headers', headers)
874
+
875
+ //query
876
+ let query = get(req, 'query')
877
+ query = iseobj(query) ? query : ''
878
+ // console.log('query', query)
879
+
880
+ //authorization
881
+ let authorization = get(headers, 'authorization', '')
882
+ authorization = isestr(authorization) ? authorization : ''
883
+
884
+ //check
885
+ if (true) {
886
+
887
+ //verifyConn
888
+ let m = verifyConn({ apiType: 'download-get-filename', authorization, headers, query })
889
+ if (ispm(m)) {
890
+ m = await m
891
+ }
892
+
893
+ //check
894
+ if (m !== true) {
895
+ return responseU8aStreamWithError(res, 'permission denied')
896
+ }
897
+
898
+ }
899
+
900
+ //eeEmit
901
+ eeEmit('handler', {
902
+ api: 'apiDownloadGetFilename',
903
+ headers,
904
+ query,
905
+ })
906
+
907
+ //fileId, 從payload接收
908
+ let fileId = get(req, 'payload.fileId', '')
909
+ // console.log('fileId', fileId)
910
+
911
+ //check
912
+ if (!isestr(fileId)) {
913
+ // console.log('invalid fileId in payload')
914
+ return responseU8aStreamWithError(res, 'invalid fileId in payload')
915
+ }
916
+
917
+ //inp
918
+ let inp = { fileId }
919
+
920
+ //procDownloadGetFilename
921
+ let out = {}
922
+ let returnType = ''
923
+ let returnMsg = ''
924
+ await procDownload(inp)
925
+ .then(function(msg) {
926
+ out.success = msg
927
+ returnType = 'success'
928
+ returnMsg = 'need to parse'
929
+ })
930
+ .catch(function(msg) {
931
+ out.error = msg
932
+ returnType = 'error'
933
+ returnMsg = 'need to parse'
934
+ })
935
+ // console.log('out', out)
936
+
937
+ //r
938
+ let r = get(out, 'success')
939
+
940
+ //streamRead
941
+ let streamRead = get(r, 'streamRead')
942
+
943
+ //destroy, 不提供stream故須預先destroy
944
+ try {
945
+ streamRead.destroy()
946
+ }
947
+ catch (err) {}
948
+
949
+ //filename
950
+ let filename = get(r, 'filename')
951
+ if (!isestr(filename)) {
952
+ //已於前面destroy
953
+ return responseU8aStreamWithError(res, 'invalid filename')
954
+ }
955
+
956
+ //重新提供out
957
+ out = {
958
+ success: {
959
+ filename,
960
+ },
961
+ }
962
+
963
+ //u8aOut
964
+ let u8aOut = obj2u8arr(out)
965
+ // console.log('u8aOut', u8aOut)
966
+
967
+ return responseU8aStream(res, u8aOut, { returnType, returnMsg })
968
+ },
969
+ }
970
+
971
+ //apiDownloadGetFile
972
+ let apiDownloadGetFile = {
973
+ path: `/${apiName}dwgf`,
974
+ method: 'GET',
975
+ options: {
976
+ timeout: {
977
+ server: false, //關閉伺服器超時
978
+ socket: false, //關閉socket超時
979
+ },
980
+ },
981
+ handler: async function (req, res) {
982
+ // console.log(req, res)
983
+ // console.log('payload', req.payload)
984
+
985
+ //headers
986
+ let headers = get(req, 'headers')
987
+ headers = iseobj(headers) ? headers : ''
988
+ // console.log('headers', headers)
989
+
990
+ //query
991
+ let query = get(req, 'query')
992
+ query = iseobj(query) ? query : ''
993
+ // console.log('query', query)
994
+
995
+ //token
996
+ let token = get(query, 'token', '')
997
+ token = isestr(token) ? token : ''
998
+ // console.log('token', token)
999
+
1000
+ //authorization
1001
+ let authorization = ''
1002
+ if (isestr(token)) {
1003
+ authorization = `${tokenType} ${token}`
1004
+ }
1005
+
1006
+ //check
1007
+ if (true) {
1008
+
1009
+ //verifyConn
1010
+ let m = verifyConn({ apiType: 'download-get-file', authorization, headers, query })
1011
+ if (ispm(m)) {
1012
+ m = await m
1013
+ }
1014
+
1015
+ //check
1016
+ if (m !== true) {
1017
+ return responseU8aStreamWithError(res, 'permission denied')
1018
+ }
1019
+
1020
+ }
1021
+
1022
+ //eeEmit
1023
+ eeEmit('handler', {
1024
+ api: 'apiDownloadGetFilename',
1025
+ headers,
1026
+ query,
1027
+ })
1028
+
1029
+ //fileId
1030
+ let fileId = get(query, 'fileId', '')
1031
+ fileId = isestr(fileId) ? fileId : ''
1032
+ // console.log('fileId', fileId)
1033
+
1034
+ //check
1035
+ if (!isestr(fileId)) {
1036
+ // console.log('invalid fileId in query')
1037
+ return responseU8aStreamWithError(res, 'invalid fileId in query')
1038
+ }
1039
+
1040
+ //inp
1041
+ let inp = { fileId }
1042
+
1043
+ //procDownload
1044
+ let out = {}
1045
+ await procDownload(inp)
1046
+ .then(function(msg) {
1047
+ out.success = msg
1048
+ })
1049
+ .catch(function(msg) {
1050
+ out.error = msg
1051
+ })
1052
+ // console.log('out', out)
1053
+
1054
+ //return
1055
+ if (haskey(out, 'error')) {
1056
+ // console.log('out.error', out.error)
1057
+ return responseU8aStreamWithError(res, `can not get file from fileId`)
1058
+ }
1059
+
1060
+ //r
1061
+ let r = get(out, 'success')
1062
+
1063
+ //streamRead
1064
+ let streamRead = get(r, 'streamRead')
1065
+
1066
+ // //filename
1067
+ // let filename = get(r, 'filename')
1068
+ // if (!isestr(filename)) {
1069
+ // return responseU8aStreamWithError(res, 'invalid filename')
1070
+ // }
1071
+ // filename = str2b64(filename) //headers內對中文支援度不佳須用base64傳
1072
+ // console.log('filename', filename)
1073
+
1074
+ //fileSize
1075
+ let fileSize = get(r, 'fileSize')
1076
+ if (!isNumber(fileSize)) {
1077
+ try {
1078
+ streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
1079
+ }
1080
+ catch (err) {}
1081
+ return responseU8aStreamWithError(res, 'invalid fileSize')
1082
+ }
1083
+ // fileSize = cstr(fileSize)
1084
+
1085
+ //fileType
1086
+ let fileType = get(r, 'fileType')
1087
+ if (!isestr(fileType)) {
1088
+ try {
1089
+ streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
1090
+ }
1091
+ catch (err) {}
1092
+ return responseU8aStreamWithError(res, 'invalid fileType')
1093
+ }
1094
+ fileType = cstr(fileType)
1095
+
1096
+ return res.response(streamRead)
1097
+ .type(fileType)
1098
+ // .header('Content-Disposition', `attachment; filename="${filename}"`) //chrome會優先使用header內filename, 但header內支援中文度很差須用base64, 此導致chrome下載檔名只能為base64, 故一律改由前端(browser)先取得真實檔名後直接給予下載檔名, 避免用header提供真實檔名
1099
+ .header('Content-Length', fileSize)
1100
+ },
1101
+ }
1102
+
821
1103
  //apiDownload
822
1104
  let apiDownload = {
823
- path: `/${apiDownloadName}`,
1105
+ path: `/${apiName}dw`,
824
1106
  method: 'POST',
825
1107
  options: {
826
1108
  payload: {
@@ -828,7 +1110,7 @@ function WConverhpServer(opt = {}) {
828
1110
  maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
829
1111
  timeout: false, //避免請求未完成時中斷
830
1112
  // output: 'stream',
831
- parse: true, //前端送obj過來
1113
+ parse: true, //前端送obj過來須自動解析
832
1114
  },
833
1115
  timeout: {
834
1116
  server: false, //關閉伺服器超時
@@ -912,16 +1194,24 @@ function WConverhpServer(opt = {}) {
912
1194
  //streamRead
913
1195
  let streamRead = get(r, 'streamRead')
914
1196
 
915
- //fileName
916
- let fileName = get(r, 'fileName')
917
- if (!isestr(fileName)) {
918
- return responseU8aStreamWithError(res, 'invalid fileName')
1197
+ //filename
1198
+ let filename = get(r, 'filename')
1199
+ if (!isestr(filename)) {
1200
+ try {
1201
+ streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
1202
+ }
1203
+ catch (err) {}
1204
+ return responseU8aStreamWithError(res, 'invalid filename')
919
1205
  }
920
- fileName = str2b64(fileName) //headers內對中文支援度不佳須用base64傳
1206
+ filename = str2b64(filename) //headers內對中文支援度不佳須用base64傳
921
1207
 
922
1208
  //fileSize
923
1209
  let fileSize = get(r, 'fileSize')
924
1210
  if (!isNumber(fileSize)) {
1211
+ try {
1212
+ streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
1213
+ }
1214
+ catch (err) {}
925
1215
  return responseU8aStreamWithError(res, 'invalid fileSize')
926
1216
  }
927
1217
  // fileSize = cstr(fileSize)
@@ -929,13 +1219,17 @@ function WConverhpServer(opt = {}) {
929
1219
  //fileType
930
1220
  let fileType = get(r, 'fileType')
931
1221
  if (!isestr(fileType)) {
1222
+ try {
1223
+ streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
1224
+ }
1225
+ catch (err) {}
932
1226
  return responseU8aStreamWithError(res, 'invalid fileType')
933
1227
  }
934
1228
  fileType = cstr(fileType)
935
1229
 
936
1230
  return res.response(streamRead)
937
1231
  .type(fileType)
938
- .header('Content-Disposition', `attachment; filename="${fileName}"`)
1232
+ .header('Content-Disposition', `attachment; filename="${filename}"`) //針對前端(nodejs)用POST下載, 可基於header內base64檔名解析出並直接給予檔名, 不用預先取得檔名
939
1233
  .header('Content-Length', fileSize)
940
1234
  },
941
1235
  }
@@ -971,6 +1265,8 @@ function WConverhpServer(opt = {}) {
971
1265
  apiMain,
972
1266
  apiUploadSlice,
973
1267
  apiUploadSliceMerge,
1268
+ apiDownloadGetFilename,
1269
+ apiDownloadGetFile,
974
1270
  apiDownload,
975
1271
  ]
976
1272
  }
@@ -987,8 +1283,7 @@ function WConverhpServer(opt = {}) {
987
1283
 
988
1284
  //start
989
1285
  if (get(opt, 'serverHapi')) {
990
- // opt.serverHapi.route([apiMain, apiUploadSlice, apiUploadSliceMerge, apiDownload])
991
- server.route([apiMain, apiUploadSlice, apiUploadSliceMerge, apiDownload])
1286
+ server.route([apiMain, apiUploadSlice, apiUploadSliceMerge, apiDownloadGetFilename, apiDownloadGetFile, apiDownload])
992
1287
  }
993
1288
  else {
994
1289
  startServer()
package/srv.mjs CHANGED
@@ -3,6 +3,7 @@ import _ from 'lodash-es'
3
3
  import w from 'wsemi'
4
4
  import WConverhpServer from './src/WConverhpServer.mjs'
5
5
 
6
+
6
7
  let ms = []
7
8
 
8
9
  let opt = {
@@ -77,29 +78,32 @@ wo.on('upload', (input, pm) => {
77
78
  wo.on('download', (input, pm) => {
78
79
  console.log(`Server[port:${opt.port}]: download`, input)
79
80
 
81
+ let streamRead = null
80
82
  try {
81
83
  ms.push({ 'download': input })
82
84
 
83
85
  //fp
84
86
  let fp = `./test/1mb.7z`
85
87
 
86
- //streamRead
87
- let streamRead = fs.createReadStream(fp)
88
-
89
- //fileName
90
- let fileName = `1mb中文.7z` //測試支援中文
88
+ //check, 檔案存在才往下
91
89
 
92
90
  //fileSize
93
91
  let stats = fs.statSync(fp)
94
92
  let fileSize = stats.size
95
93
 
94
+ //streamRead
95
+ streamRead = fs.createReadStream(fp)
96
+
97
+ //filename
98
+ let filename = `1mb中文.7z` //測試支援中文
99
+
96
100
  //fileType
97
101
  let fileType = 'application/x-7z-compressed'
98
102
 
99
103
  //output
100
104
  let output = {
101
105
  streamRead,
102
- fileName,
106
+ filename,
103
107
  fileSize,
104
108
  fileType,
105
109
  }
@@ -108,6 +112,10 @@ wo.on('download', (input, pm) => {
108
112
  }
109
113
  catch (err) {
110
114
  console.log('download error', err)
115
+ // try {
116
+ // streamRead.destroy() //若fs.createReadStream早於fs.statSync執行, 但fs.statSync發生錯誤時, stream得要destroy
117
+ // }
118
+ // catch (err) {}
111
119
  pm.reject('download error')
112
120
  }
113
121
 
@@ -123,7 +131,7 @@ setTimeout(() => {
123
131
  console.log('ms', ms)
124
132
  // console.log('ms', JSON.stringify(ms))
125
133
  wo.stop()
126
- }, 1113000)
134
+ }, 3000)
127
135
 
128
136
 
129
137
  //node --experimental-modules srv.mjs