@@ -2,6 +2,8 @@ import _ from 'lodash';
22import { diffConvertYaml } from '@serverless-devs/diff' ;
33import inquirer from 'inquirer' ;
44import fs from 'fs' ;
5+ import os from 'os' ;
6+ import axios from 'axios' ;
57import assert from 'assert' ;
68import path from 'path' ;
79import { yellow } from 'chalk' ;
@@ -277,12 +279,16 @@ export default class Service extends Base {
277279 return true ;
278280 }
279281
280- let zipPath : string = path . isAbsolute ( codeUri )
281- ? codeUri
282- : path . join ( this . inputs . baseDir , codeUri ) ;
282+ let zipPath : string ;
283+ // 处理不同类型的 codeUri
284+ if ( codeUri . startsWith ( 'http://' ) || codeUri . startsWith ( 'https://' ) ) {
285+ zipPath = await this . _downloadFromUrl ( codeUri ) ;
286+ } else {
287+ zipPath = path . isAbsolute ( codeUri ) ? codeUri : path . join ( this . inputs . baseDir , codeUri ) ;
288+ }
283289 logger . debug ( `Code path absolute path: ${ zipPath } ` ) ;
284290
285- const needZip = this . _assertNeedZip ( codeUri ) ;
291+ const needZip = this . _assertNeedZip ( zipPath ) ;
286292 logger . debug ( `Need zip file: ${ needZip } ` ) ;
287293
288294 let generateZipFilePath = '' ;
@@ -313,6 +319,15 @@ export default class Service extends Base {
313319 logger . debug (
314320 yellow ( `skip uploadCode because code is no changed, codeChecksum=${ crc64Value } ` ) ,
315321 ) ;
322+ // 清理临时文件
323+ if ( generateZipFilePath ) {
324+ try {
325+ fs . rmSync ( generateZipFilePath ) ;
326+ } catch ( ex ) {
327+ logger . debug ( `Unable to remove zip file: ${ zipPath } ` ) ;
328+ }
329+ }
330+
316331 return false ;
317332 } else {
318333 logger . debug ( `\x1b[33mcodeChecksum from ${ this . codeChecksum } to ${ crc64Value } \x1b[0m` ) ;
@@ -333,6 +348,67 @@ export default class Service extends Base {
333348 return true ;
334349 }
335350
351+ /**
352+ * 从URL下载文件到本地临时目录
353+ */
354+ private async _downloadFromUrl ( url : string ) : Promise < string > {
355+ logger . warn ( `Downloading code from URL: ${ url } ` ) ;
356+
357+ // 创建临时目录
358+ const tempDir = path . join ( os . tmpdir ( ) , 'fc_code_download' , Date . now ( ) . toString ( ) ) ;
359+ fs . mkdirSync ( tempDir , { recursive : true } ) ;
360+ let downloadPath : string ;
361+
362+ try {
363+ // 从URL获取文件名
364+ const urlPath = new URL ( url ) . pathname ;
365+ const filename = path . basename ( urlPath ) || `downloaded_code_${ Date . now ( ) } ` ;
366+ downloadPath = path . join ( tempDir , filename ) ;
367+
368+ // 下载文件
369+ const response = await axios ( {
370+ method : 'GET' ,
371+ url,
372+ responseType : 'stream' ,
373+ } ) ;
374+
375+ const writer = fs . createWriteStream ( downloadPath ) ;
376+
377+ // 创建一个 Promise 来处理流操作
378+ await new Promise < void > ( ( resolve , reject ) => {
379+ response . data . on ( 'error' , ( err ) => {
380+ reject ( new Error ( `Source stream error: ${ err . message } ` ) ) ;
381+ // 确保 writer 被关闭
382+ writer . destroy ( err ) ;
383+ } ) ;
384+
385+ writer . on ( 'finish' , resolve ) ;
386+
387+ writer . on ( 'error' , ( err ) => {
388+ reject ( new Error ( `Write stream error: ${ err . message } ` ) ) ;
389+ response . data . destroy ( ) ;
390+ } ) ;
391+
392+ response . data . pipe ( writer ) ;
393+ } ) ;
394+
395+ logger . debug ( `Downloaded file to: ${ downloadPath } ` ) ;
396+
397+ // 返回下载文件路径,由主流程决定是否需要压缩
398+ return downloadPath ;
399+ } catch ( error ) {
400+ // 如果下载失败,清理临时目录
401+ try {
402+ fs . rmSync ( tempDir , { recursive : true , force : true } ) ;
403+ logger . debug ( `Cleaned up temporary directory after error: ${ tempDir } ` ) ;
404+ } catch ( cleanupError ) {
405+ logger . debug ( `Failed to clean up temporary directory: ${ cleanupError . message } ` ) ;
406+ }
407+
408+ throw new Error ( `Failed to download code from URL: ${ error . message } ` ) ;
409+ }
410+ }
411+
336412 /**
337413 * 生成 auto 资源,非 FC 资源,主要指 vpc、nas、log、role(oss mount 挂载点才有)
338414 */
0 commit comments