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.
- package/README.md +58 -104
- package/dist/w-converhp-client.umd.js +2 -2
- package/dist/w-converhp-client.umd.js.map +1 -1
- package/dist/w-converhp-server.umd.js +2 -2
- package/dist/w-converhp-server.umd.js.map +1 -1
- package/docs/WConverhpClient.html +3 -3
- package/docs/WConverhpClient.mjs.html +102 -119
- package/docs/WConverhpServer.html +43 -3
- package/docs/WConverhpServer.mjs.html +330 -35
- package/docs/index.html +1 -1
- package/package.json +2 -2
- package/scld.mjs +4 -4
- package/src/WConverhpClient.mjs +101 -118
- package/src/WConverhpServer.mjs +329 -34
- package/srv.mjs +15 -7
- package/test/downloadLargeFile.test.mjs +24 -19
- package/test/executeWithFile.test.mjs +10 -12
- package/test/executeWithU8a.test.mjs +11 -13
- package/test/uploadLargeFile.test.mjs +10 -14
|
@@ -73,6 +73,11 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
73
73
|
// import mergeFiles from './mergeFiles.mjs'
|
|
74
74
|
|
|
75
75
|
|
|
76
|
+
//回傳前端stream時(POST或GET皆可), 前端會須等stream傳完才能判斷是否為大檔或錯誤訊息, 此會導致若回傳超大檔, 會需要對超大檔進行解析會有記憶體上限問題, 故需要通過header提供基本成功或失敗訊息, 讓前端能進行解析判斷
|
|
77
|
+
//回傳前端(nodejs)時, 針對超大檔, 只能用POST並用stream回傳
|
|
78
|
+
//回傳前端(browser)時, 針對超大檔, 可用POST並用stream回傳但還要處理進度條, 若要交由瀏覽器下載器處理, 只能用GET並用stream回傳, 且前端只能用window.location.href或a.href+a.click()下載
|
|
79
|
+
|
|
80
|
+
|
|
76
81
|
/**
|
|
77
82
|
* 建立Hapi伺服器
|
|
78
83
|
*
|
|
@@ -83,6 +88,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
83
88
|
* @param {String} [opt.pathStaticFiles='dist'] 輸入當useInert=true時提供瀏覽資料夾字串,預設'dist'
|
|
84
89
|
* @param {String} [opt.apiName='api'] 輸入API名稱字串,預設'api'
|
|
85
90
|
* @param {String} [opt.pathUploadTemp='./uploadTemp'] 輸入暫時存放切片上傳檔案資料夾字串,預設'./uploadTemp'
|
|
91
|
+
* @param {String} [opt.tokenType='Bearer'] 輸入token類型字串,預設'Bearer'
|
|
86
92
|
* @param {Function} [opt.verifyConn=()=>{return true}] 輸入呼叫API時檢測函數,預設()=>{return true}
|
|
87
93
|
* @param {Array} [opt.corsOrigins=['*']] 輸入允許跨域網域陣列,若給予['*']代表允許全部,預設['*']
|
|
88
94
|
* @param {Integer} [opt.delayForBasic=0] 輸入基本API用延遲響應時間,單位ms,預設0
|
|
@@ -91,14 +97,24 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
91
97
|
* @returns {Object} 回傳事件物件,可監聽事件execute、upload、handler
|
|
92
98
|
* @example
|
|
93
99
|
*
|
|
100
|
+
* import fs from 'fs'
|
|
94
101
|
* import _ from 'lodash-es'
|
|
95
|
-
* import
|
|
102
|
+
* import w from 'wsemi'
|
|
103
|
+
* import WConverhpServer from './src/WConverhpServer.mjs'
|
|
104
|
+
*
|
|
105
|
+
* let ms = []
|
|
96
106
|
*
|
|
97
107
|
* let opt = {
|
|
98
108
|
* port: 8080,
|
|
99
109
|
* apiName: 'api',
|
|
100
110
|
* pathStaticFiles: '.', //要存取專案資料夾下web.html, 故不能給dist
|
|
101
|
-
* verifyConn: () => {
|
|
111
|
+
* verifyConn: async ({ apiType, authorization, headers, query }) => {
|
|
112
|
+
* console.log('verifyConn', `apiType[${apiType}]`, `authorization[${authorization}]`)
|
|
113
|
+
* let token = w.strdelleft(authorization, 7) //刪除Bearer
|
|
114
|
+
* if (!w.isestr(token)) {
|
|
115
|
+
* return false
|
|
116
|
+
* }
|
|
117
|
+
* // await w.delay(3000)
|
|
102
118
|
* return true
|
|
103
119
|
* },
|
|
104
120
|
* }
|
|
@@ -116,6 +132,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
116
132
|
*
|
|
117
133
|
* if (_.get(input, 'p.d.u8a', null)) {
|
|
118
134
|
* console.log('input.p.d.u8a', input.p.d.u8a)
|
|
135
|
+
* ms.push({ 'input.p.d.u8a': input.p.d.u8a })
|
|
119
136
|
* }
|
|
120
137
|
*
|
|
121
138
|
* let r = {
|
|
@@ -123,13 +140,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
123
140
|
* _data: [11, 22.22, 'abc', { x: '21', y: 65.43, z: 'test中文' }],
|
|
124
141
|
* _bin: {
|
|
125
142
|
* name: 'zdata.b2',
|
|
126
|
-
* u8a: new Uint8Array([66, 97, 115]),
|
|
127
|
-
* // name: '100mb.7z',
|
|
128
|
-
* // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\100mb.7z')),
|
|
129
|
-
* // name: '500mb.7z',
|
|
130
|
-
* // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\500mb.7z')),
|
|
131
|
-
* // name: '1000mb.7z',
|
|
132
|
-
* // u8a: new Uint8Array(fs.readFileSync('D:\\開源-JS-006-2-w-converhp\\_temp\\1000mb.7z')),
|
|
143
|
+
* u8a: new Uint8Array([52, 66, 97, 115]),
|
|
133
144
|
* },
|
|
134
145
|
* }
|
|
135
146
|
*
|
|
@@ -152,6 +163,7 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
152
163
|
* console.log(`Server[port:${opt.port}]: upload`, input)
|
|
153
164
|
*
|
|
154
165
|
* try {
|
|
166
|
+
* ms.push({ 'receive and return': input })
|
|
155
167
|
* let output = input
|
|
156
168
|
* pm.resolve(output)
|
|
157
169
|
* }
|
|
@@ -164,29 +176,32 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
164
176
|
* wo.on('download', (input, pm) => {
|
|
165
177
|
* console.log(`Server[port:${opt.port}]: download`, input)
|
|
166
178
|
*
|
|
179
|
+
* let streamRead = null
|
|
167
180
|
* try {
|
|
168
181
|
* ms.push({ 'download': input })
|
|
169
182
|
*
|
|
170
183
|
* //fp
|
|
171
184
|
* let fp = `./test/1mb.7z`
|
|
172
185
|
*
|
|
173
|
-
* //
|
|
174
|
-
* let streamRead = fs.createReadStream(fp)
|
|
175
|
-
*
|
|
176
|
-
* //fileName
|
|
177
|
-
* let fileName = `1mb.7z`
|
|
186
|
+
* //check, 檔案存在才往下
|
|
178
187
|
*
|
|
179
188
|
* //fileSize
|
|
180
189
|
* let stats = fs.statSync(fp)
|
|
181
190
|
* let fileSize = stats.size
|
|
182
191
|
*
|
|
192
|
+
* //streamRead
|
|
193
|
+
* streamRead = fs.createReadStream(fp)
|
|
194
|
+
*
|
|
195
|
+
* //filename
|
|
196
|
+
* let filename = `1mb中文.7z` //測試支援中文
|
|
197
|
+
*
|
|
183
198
|
* //fileType
|
|
184
199
|
* let fileType = 'application/x-7z-compressed'
|
|
185
200
|
*
|
|
186
201
|
* //output
|
|
187
202
|
* let output = {
|
|
188
203
|
* streamRead,
|
|
189
|
-
*
|
|
204
|
+
* filename,
|
|
190
205
|
* fileSize,
|
|
191
206
|
* fileType,
|
|
192
207
|
* }
|
|
@@ -195,14 +210,27 @@ import mergeFiles from './mergeFiles.wk.umd.js'
|
|
|
195
210
|
* }
|
|
196
211
|
* catch (err) {
|
|
197
212
|
* console.log('download error', err)
|
|
213
|
+
* // try {
|
|
214
|
+
* // streamRead.destroy() //若fs.createReadStream早於fs.statSync執行, 但fs.statSync發生錯誤時, stream得要destroy
|
|
215
|
+
* // }
|
|
216
|
+
* // catch (err) {}
|
|
198
217
|
* pm.reject('download error')
|
|
199
218
|
* }
|
|
200
219
|
*
|
|
201
220
|
* })
|
|
221
|
+
* wo.on('error', (err) => {
|
|
222
|
+
* console.log(`Server[port:${opt.port}]: error`, err)
|
|
223
|
+
* })
|
|
202
224
|
* wo.on('handler', (data) => {
|
|
203
225
|
* // console.log(`Server[port:${opt.port}]: handler`, data)
|
|
204
226
|
* })
|
|
205
227
|
*
|
|
228
|
+
* setTimeout(() => {
|
|
229
|
+
* console.log('ms', ms)
|
|
230
|
+
* // console.log('ms', JSON.stringify(ms))
|
|
231
|
+
* wo.stop()
|
|
232
|
+
* }, 3000)
|
|
233
|
+
*
|
|
206
234
|
*/
|
|
207
235
|
function WConverhpServer(opt = {}) {
|
|
208
236
|
|
|
@@ -230,12 +258,6 @@ function WConverhpServer(opt = {}) {
|
|
|
230
258
|
apiName = 'api'
|
|
231
259
|
}
|
|
232
260
|
|
|
233
|
-
//apiUploadSliceName
|
|
234
|
-
let apiUploadSliceName = `${apiName}slc`
|
|
235
|
-
|
|
236
|
-
//apiDownloadName
|
|
237
|
-
let apiDownloadName = `${apiName}dw`
|
|
238
|
-
|
|
239
261
|
//pathUploadTemp
|
|
240
262
|
let pathUploadTemp = get(opt, 'pathUploadTemp')
|
|
241
263
|
if (!isestr(pathUploadTemp)) {
|
|
@@ -245,6 +267,12 @@ function WConverhpServer(opt = {}) {
|
|
|
245
267
|
fsCreateFolder(pathUploadTemp)
|
|
246
268
|
}
|
|
247
269
|
|
|
270
|
+
//tokenType
|
|
271
|
+
let tokenType = get(opt, 'tokenType')
|
|
272
|
+
if (!isestr(tokenType)) {
|
|
273
|
+
tokenType = 'Bearer'
|
|
274
|
+
}
|
|
275
|
+
|
|
248
276
|
//verifyConn
|
|
249
277
|
let verifyConn = get(opt, 'verifyConn')
|
|
250
278
|
if (!isfun(verifyConn)) {
|
|
@@ -605,7 +633,7 @@ function WConverhpServer(opt = {}) {
|
|
|
605
633
|
|
|
606
634
|
//apiUploadSlice
|
|
607
635
|
let apiUploadSlice = {
|
|
608
|
-
path: `/${
|
|
636
|
+
path: `/${apiName}slc`,
|
|
609
637
|
method: 'POST',
|
|
610
638
|
options: {
|
|
611
639
|
payload: {
|
|
@@ -747,7 +775,7 @@ function WConverhpServer(opt = {}) {
|
|
|
747
775
|
|
|
748
776
|
//apiUploadSliceMerge
|
|
749
777
|
let apiUploadSliceMerge = {
|
|
750
|
-
path: `/${
|
|
778
|
+
path: `/${apiName}slcm`,
|
|
751
779
|
method: 'POST',
|
|
752
780
|
options: {
|
|
753
781
|
payload: {
|
|
@@ -755,7 +783,7 @@ function WConverhpServer(opt = {}) {
|
|
|
755
783
|
maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
|
|
756
784
|
timeout: false, //避免請求未完成時中斷
|
|
757
785
|
// output: 'stream',
|
|
758
|
-
parse: true, //前端送obj
|
|
786
|
+
parse: true, //前端送obj過來須自動解析
|
|
759
787
|
},
|
|
760
788
|
timeout: {
|
|
761
789
|
server: false, //關閉伺服器超時
|
|
@@ -865,9 +893,263 @@ function WConverhpServer(opt = {}) {
|
|
|
865
893
|
},
|
|
866
894
|
}
|
|
867
895
|
|
|
896
|
+
//apiDownloadGetFilename
|
|
897
|
+
let apiDownloadGetFilename = {
|
|
898
|
+
path: `/${apiName}dwgfn`,
|
|
899
|
+
method: 'POST',
|
|
900
|
+
options: {
|
|
901
|
+
payload: {
|
|
902
|
+
maxBytes: 1024 * 1024 * 1024 * 1024, //預設為1mb, 調整至1tb, 也就是給予3次方
|
|
903
|
+
maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
|
|
904
|
+
timeout: false, //避免請求未完成時中斷
|
|
905
|
+
// output: 'stream',
|
|
906
|
+
parse: true, //前端送obj過來須自動解析
|
|
907
|
+
},
|
|
908
|
+
timeout: {
|
|
909
|
+
server: false, //關閉伺服器超時
|
|
910
|
+
socket: false, //關閉socket超時
|
|
911
|
+
},
|
|
912
|
+
},
|
|
913
|
+
handler: async function (req, res) {
|
|
914
|
+
// console.log(req, res)
|
|
915
|
+
// console.log('payload', req.payload)
|
|
916
|
+
|
|
917
|
+
//headers
|
|
918
|
+
let headers = get(req, 'headers')
|
|
919
|
+
headers = iseobj(headers) ? headers : ''
|
|
920
|
+
// console.log('headers', headers)
|
|
921
|
+
|
|
922
|
+
//query
|
|
923
|
+
let query = get(req, 'query')
|
|
924
|
+
query = iseobj(query) ? query : ''
|
|
925
|
+
// console.log('query', query)
|
|
926
|
+
|
|
927
|
+
//authorization
|
|
928
|
+
let authorization = get(headers, 'authorization', '')
|
|
929
|
+
authorization = isestr(authorization) ? authorization : ''
|
|
930
|
+
|
|
931
|
+
//check
|
|
932
|
+
if (true) {
|
|
933
|
+
|
|
934
|
+
//verifyConn
|
|
935
|
+
let m = verifyConn({ apiType: 'download-get-filename', authorization, headers, query })
|
|
936
|
+
if (ispm(m)) {
|
|
937
|
+
m = await m
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
//check
|
|
941
|
+
if (m !== true) {
|
|
942
|
+
return responseU8aStreamWithError(res, 'permission denied')
|
|
943
|
+
}
|
|
944
|
+
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
//eeEmit
|
|
948
|
+
eeEmit('handler', {
|
|
949
|
+
api: 'apiDownloadGetFilename',
|
|
950
|
+
headers,
|
|
951
|
+
query,
|
|
952
|
+
})
|
|
953
|
+
|
|
954
|
+
//fileId, 從payload接收
|
|
955
|
+
let fileId = get(req, 'payload.fileId', '')
|
|
956
|
+
// console.log('fileId', fileId)
|
|
957
|
+
|
|
958
|
+
//check
|
|
959
|
+
if (!isestr(fileId)) {
|
|
960
|
+
// console.log('invalid fileId in payload')
|
|
961
|
+
return responseU8aStreamWithError(res, 'invalid fileId in payload')
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
//inp
|
|
965
|
+
let inp = { fileId }
|
|
966
|
+
|
|
967
|
+
//procDownloadGetFilename
|
|
968
|
+
let out = {}
|
|
969
|
+
let returnType = ''
|
|
970
|
+
let returnMsg = ''
|
|
971
|
+
await procDownload(inp)
|
|
972
|
+
.then(function(msg) {
|
|
973
|
+
out.success = msg
|
|
974
|
+
returnType = 'success'
|
|
975
|
+
returnMsg = 'need to parse'
|
|
976
|
+
})
|
|
977
|
+
.catch(function(msg) {
|
|
978
|
+
out.error = msg
|
|
979
|
+
returnType = 'error'
|
|
980
|
+
returnMsg = 'need to parse'
|
|
981
|
+
})
|
|
982
|
+
// console.log('out', out)
|
|
983
|
+
|
|
984
|
+
//r
|
|
985
|
+
let r = get(out, 'success')
|
|
986
|
+
|
|
987
|
+
//streamRead
|
|
988
|
+
let streamRead = get(r, 'streamRead')
|
|
989
|
+
|
|
990
|
+
//destroy, 不提供stream故須預先destroy
|
|
991
|
+
try {
|
|
992
|
+
streamRead.destroy()
|
|
993
|
+
}
|
|
994
|
+
catch (err) {}
|
|
995
|
+
|
|
996
|
+
//filename
|
|
997
|
+
let filename = get(r, 'filename')
|
|
998
|
+
if (!isestr(filename)) {
|
|
999
|
+
//已於前面destroy
|
|
1000
|
+
return responseU8aStreamWithError(res, 'invalid filename')
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
//重新提供out
|
|
1004
|
+
out = {
|
|
1005
|
+
success: {
|
|
1006
|
+
filename,
|
|
1007
|
+
},
|
|
1008
|
+
}
|
|
1009
|
+
|
|
1010
|
+
//u8aOut
|
|
1011
|
+
let u8aOut = obj2u8arr(out)
|
|
1012
|
+
// console.log('u8aOut', u8aOut)
|
|
1013
|
+
|
|
1014
|
+
return responseU8aStream(res, u8aOut, { returnType, returnMsg })
|
|
1015
|
+
},
|
|
1016
|
+
}
|
|
1017
|
+
|
|
1018
|
+
//apiDownloadGetFile
|
|
1019
|
+
let apiDownloadGetFile = {
|
|
1020
|
+
path: `/${apiName}dwgf`,
|
|
1021
|
+
method: 'GET',
|
|
1022
|
+
options: {
|
|
1023
|
+
timeout: {
|
|
1024
|
+
server: false, //關閉伺服器超時
|
|
1025
|
+
socket: false, //關閉socket超時
|
|
1026
|
+
},
|
|
1027
|
+
},
|
|
1028
|
+
handler: async function (req, res) {
|
|
1029
|
+
// console.log(req, res)
|
|
1030
|
+
// console.log('payload', req.payload)
|
|
1031
|
+
|
|
1032
|
+
//headers
|
|
1033
|
+
let headers = get(req, 'headers')
|
|
1034
|
+
headers = iseobj(headers) ? headers : ''
|
|
1035
|
+
// console.log('headers', headers)
|
|
1036
|
+
|
|
1037
|
+
//query
|
|
1038
|
+
let query = get(req, 'query')
|
|
1039
|
+
query = iseobj(query) ? query : ''
|
|
1040
|
+
// console.log('query', query)
|
|
1041
|
+
|
|
1042
|
+
//token
|
|
1043
|
+
let token = get(query, 'token', '')
|
|
1044
|
+
token = isestr(token) ? token : ''
|
|
1045
|
+
// console.log('token', token)
|
|
1046
|
+
|
|
1047
|
+
//authorization
|
|
1048
|
+
let authorization = ''
|
|
1049
|
+
if (isestr(token)) {
|
|
1050
|
+
authorization = `${tokenType} ${token}`
|
|
1051
|
+
}
|
|
1052
|
+
|
|
1053
|
+
//check
|
|
1054
|
+
if (true) {
|
|
1055
|
+
|
|
1056
|
+
//verifyConn
|
|
1057
|
+
let m = verifyConn({ apiType: 'download-get-file', authorization, headers, query })
|
|
1058
|
+
if (ispm(m)) {
|
|
1059
|
+
m = await m
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
//check
|
|
1063
|
+
if (m !== true) {
|
|
1064
|
+
return responseU8aStreamWithError(res, 'permission denied')
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
//eeEmit
|
|
1070
|
+
eeEmit('handler', {
|
|
1071
|
+
api: 'apiDownloadGetFilename',
|
|
1072
|
+
headers,
|
|
1073
|
+
query,
|
|
1074
|
+
})
|
|
1075
|
+
|
|
1076
|
+
//fileId
|
|
1077
|
+
let fileId = get(query, 'fileId', '')
|
|
1078
|
+
fileId = isestr(fileId) ? fileId : ''
|
|
1079
|
+
// console.log('fileId', fileId)
|
|
1080
|
+
|
|
1081
|
+
//check
|
|
1082
|
+
if (!isestr(fileId)) {
|
|
1083
|
+
// console.log('invalid fileId in query')
|
|
1084
|
+
return responseU8aStreamWithError(res, 'invalid fileId in query')
|
|
1085
|
+
}
|
|
1086
|
+
|
|
1087
|
+
//inp
|
|
1088
|
+
let inp = { fileId }
|
|
1089
|
+
|
|
1090
|
+
//procDownload
|
|
1091
|
+
let out = {}
|
|
1092
|
+
await procDownload(inp)
|
|
1093
|
+
.then(function(msg) {
|
|
1094
|
+
out.success = msg
|
|
1095
|
+
})
|
|
1096
|
+
.catch(function(msg) {
|
|
1097
|
+
out.error = msg
|
|
1098
|
+
})
|
|
1099
|
+
// console.log('out', out)
|
|
1100
|
+
|
|
1101
|
+
//return
|
|
1102
|
+
if (haskey(out, 'error')) {
|
|
1103
|
+
// console.log('out.error', out.error)
|
|
1104
|
+
return responseU8aStreamWithError(res, `can not get file from fileId`)
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
//r
|
|
1108
|
+
let r = get(out, 'success')
|
|
1109
|
+
|
|
1110
|
+
//streamRead
|
|
1111
|
+
let streamRead = get(r, 'streamRead')
|
|
1112
|
+
|
|
1113
|
+
// //filename
|
|
1114
|
+
// let filename = get(r, 'filename')
|
|
1115
|
+
// if (!isestr(filename)) {
|
|
1116
|
+
// return responseU8aStreamWithError(res, 'invalid filename')
|
|
1117
|
+
// }
|
|
1118
|
+
// filename = str2b64(filename) //headers內對中文支援度不佳須用base64傳
|
|
1119
|
+
// console.log('filename', filename)
|
|
1120
|
+
|
|
1121
|
+
//fileSize
|
|
1122
|
+
let fileSize = get(r, 'fileSize')
|
|
1123
|
+
if (!isNumber(fileSize)) {
|
|
1124
|
+
try {
|
|
1125
|
+
streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
|
|
1126
|
+
}
|
|
1127
|
+
catch (err) {}
|
|
1128
|
+
return responseU8aStreamWithError(res, 'invalid fileSize')
|
|
1129
|
+
}
|
|
1130
|
+
// fileSize = cstr(fileSize)
|
|
1131
|
+
|
|
1132
|
+
//fileType
|
|
1133
|
+
let fileType = get(r, 'fileType')
|
|
1134
|
+
if (!isestr(fileType)) {
|
|
1135
|
+
try {
|
|
1136
|
+
streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
|
|
1137
|
+
}
|
|
1138
|
+
catch (err) {}
|
|
1139
|
+
return responseU8aStreamWithError(res, 'invalid fileType')
|
|
1140
|
+
}
|
|
1141
|
+
fileType = cstr(fileType)
|
|
1142
|
+
|
|
1143
|
+
return res.response(streamRead)
|
|
1144
|
+
.type(fileType)
|
|
1145
|
+
// .header('Content-Disposition', `attachment; filename="${filename}"`) //chrome會優先使用header內filename, 但header內支援中文度很差須用base64, 此導致chrome下載檔名只能為base64, 故一律改由前端(browser)先取得真實檔名後直接給予下載檔名, 避免用header提供真實檔名
|
|
1146
|
+
.header('Content-Length', fileSize)
|
|
1147
|
+
},
|
|
1148
|
+
}
|
|
1149
|
+
|
|
868
1150
|
//apiDownload
|
|
869
1151
|
let apiDownload = {
|
|
870
|
-
path: `/${
|
|
1152
|
+
path: `/${apiName}dw`,
|
|
871
1153
|
method: 'POST',
|
|
872
1154
|
options: {
|
|
873
1155
|
payload: {
|
|
@@ -875,7 +1157,7 @@ function WConverhpServer(opt = {}) {
|
|
|
875
1157
|
maxParts: 1000 * 1000 * 1000, //預設為1000, 給予3次方
|
|
876
1158
|
timeout: false, //避免請求未完成時中斷
|
|
877
1159
|
// output: 'stream',
|
|
878
|
-
parse: true, //前端送obj
|
|
1160
|
+
parse: true, //前端送obj過來須自動解析
|
|
879
1161
|
},
|
|
880
1162
|
timeout: {
|
|
881
1163
|
server: false, //關閉伺服器超時
|
|
@@ -959,16 +1241,24 @@ function WConverhpServer(opt = {}) {
|
|
|
959
1241
|
//streamRead
|
|
960
1242
|
let streamRead = get(r, 'streamRead')
|
|
961
1243
|
|
|
962
|
-
//
|
|
963
|
-
let
|
|
964
|
-
if (!isestr(
|
|
965
|
-
|
|
1244
|
+
//filename
|
|
1245
|
+
let filename = get(r, 'filename')
|
|
1246
|
+
if (!isestr(filename)) {
|
|
1247
|
+
try {
|
|
1248
|
+
streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
|
|
1249
|
+
}
|
|
1250
|
+
catch (err) {}
|
|
1251
|
+
return responseU8aStreamWithError(res, 'invalid filename')
|
|
966
1252
|
}
|
|
967
|
-
|
|
1253
|
+
filename = str2b64(filename) //headers內對中文支援度不佳須用base64傳
|
|
968
1254
|
|
|
969
1255
|
//fileSize
|
|
970
1256
|
let fileSize = get(r, 'fileSize')
|
|
971
1257
|
if (!isNumber(fileSize)) {
|
|
1258
|
+
try {
|
|
1259
|
+
streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
|
|
1260
|
+
}
|
|
1261
|
+
catch (err) {}
|
|
972
1262
|
return responseU8aStreamWithError(res, 'invalid fileSize')
|
|
973
1263
|
}
|
|
974
1264
|
// fileSize = cstr(fileSize)
|
|
@@ -976,13 +1266,17 @@ function WConverhpServer(opt = {}) {
|
|
|
976
1266
|
//fileType
|
|
977
1267
|
let fileType = get(r, 'fileType')
|
|
978
1268
|
if (!isestr(fileType)) {
|
|
1269
|
+
try {
|
|
1270
|
+
streamRead.destroy() //提供stream前發生錯誤, 得強制destroy
|
|
1271
|
+
}
|
|
1272
|
+
catch (err) {}
|
|
979
1273
|
return responseU8aStreamWithError(res, 'invalid fileType')
|
|
980
1274
|
}
|
|
981
1275
|
fileType = cstr(fileType)
|
|
982
1276
|
|
|
983
1277
|
return res.response(streamRead)
|
|
984
1278
|
.type(fileType)
|
|
985
|
-
.header('Content-Disposition', `attachment; filename="${
|
|
1279
|
+
.header('Content-Disposition', `attachment; filename="${filename}"`) //針對前端(nodejs)用POST下載, 可基於header內base64檔名解析出並直接給予檔名, 不用預先取得檔名
|
|
986
1280
|
.header('Content-Length', fileSize)
|
|
987
1281
|
},
|
|
988
1282
|
}
|
|
@@ -1018,6 +1312,8 @@ function WConverhpServer(opt = {}) {
|
|
|
1018
1312
|
apiMain,
|
|
1019
1313
|
apiUploadSlice,
|
|
1020
1314
|
apiUploadSliceMerge,
|
|
1315
|
+
apiDownloadGetFilename,
|
|
1316
|
+
apiDownloadGetFile,
|
|
1021
1317
|
apiDownload,
|
|
1022
1318
|
]
|
|
1023
1319
|
}
|
|
@@ -1034,8 +1330,7 @@ function WConverhpServer(opt = {}) {
|
|
|
1034
1330
|
|
|
1035
1331
|
//start
|
|
1036
1332
|
if (get(opt, 'serverHapi')) {
|
|
1037
|
-
|
|
1038
|
-
server.route([apiMain, apiUploadSlice, apiUploadSliceMerge, apiDownload])
|
|
1333
|
+
server.route([apiMain, apiUploadSlice, apiUploadSliceMerge, apiDownloadGetFilename, apiDownloadGetFile, apiDownload])
|
|
1039
1334
|
}
|
|
1040
1335
|
else {
|
|
1041
1336
|
startServer()
|
|
@@ -1068,7 +1363,7 @@ export default WConverhpServer
|
|
|
1068
1363
|
<br class="clear">
|
|
1069
1364
|
|
|
1070
1365
|
<footer>
|
|
1071
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on
|
|
1366
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Wed Apr 09 2025 14:36:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
1072
1367
|
</footer>
|
|
1073
1368
|
|
|
1074
1369
|
<script>prettyPrint();</script>
|
package/docs/index.html
CHANGED
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
<br class="clear">
|
|
72
72
|
|
|
73
73
|
<footer>
|
|
74
|
-
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on
|
|
74
|
+
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 4.0.2</a> on Wed Apr 09 2025 14:36:52 GMT+0800 (台北標準時間) using the <a href="https://github.com/clenemt/docdash">docdash</a> theme.
|
|
75
75
|
</footer>
|
|
76
76
|
|
|
77
77
|
<script>prettyPrint();</script>
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "w-converhp",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.16",
|
|
4
4
|
"main": "dist/w-converhp-server.umd.js",
|
|
5
5
|
"dependencies": {
|
|
6
6
|
"@hapi/hapi": "^21.4.0",
|
|
7
7
|
"@hapi/inert": "^7.1.0",
|
|
8
8
|
"axios": "^1.8.4",
|
|
9
9
|
"lodash-es": "^4.17.21",
|
|
10
|
-
"wsemi": "^1.7.
|
|
10
|
+
"wsemi": "^1.7.77"
|
|
11
11
|
},
|
|
12
12
|
"devDependencies": {
|
|
13
13
|
"form-data": "^4.0.2",
|
package/scld.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import path from 'path'
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import _ from 'lodash-es'
|
|
4
|
-
import w from 'wsemi'
|
|
1
|
+
// import path from 'path'
|
|
2
|
+
// import fs from 'fs'
|
|
3
|
+
// import _ from 'lodash-es'
|
|
4
|
+
// import w from 'wsemi'
|
|
5
5
|
import FormData from 'form-data'
|
|
6
6
|
import WConverhpClient from './src/WConverhpClient.mjs'
|
|
7
7
|
|