1.全栈工程师需要学什么
2.NodeJs全栈创建多文件断点续传
3.MidwayJS 全栈开发(一)技术选型
全栈工程师需要学什么
一、全栈全栈工程师需要学什么?
需要学习的项目内容包含但不限于以下:
1.PC、H5、源码Nodejs、全栈小程序、项目移动端,源码城信app源码掌握大前端所有技术栈;
2.能够实现类Element-ui组件库,全栈设计Vue组件;
3.掌握Vue/React源码,项目MVVM库原理;
4.了解Koa2源码,源码定制MVC开发框架;
5.前端监控、全栈性能优化、项目安全;
6.自动化测试、源码发布、全栈运维。项目
二、源码什么是全栈工程师?
全栈工程师(Full-Stack Engineer),也叫全端工程师,指的是掌握多种技能,对前端知识和后端架构都有深入的了解,能处理数据库 、服务器 、系统工程和客户端的所有工作的工程师,并拥有足够的学习能力,能利用多种技能解决问题,独立完成产品的人。
NodeJs全栈创建多文件断点续传
文章涉及的源代码在GitHub上,查看源代码。项目安装这是一个Node项目,可以使用常规的方式进行设置,如果已经有一个项目,源码人员则可以继续执行该项目,完全没有问题。如果是全新开始,请执行以下操作:
下载并安装Node,它会全局上安装npm;
安装Yeoman,npminstall-gyo,并全局安装脚手架npminstall-ggenerator-norm;
创建项目文件所在的文件夹;
打开终端并使用cd命令导航到项目目录,例如cdmultifile-uploader;
运行命令npminit-y初始化npm项目,生成简单的package.json;
运行命令yonorm初始化项目基础依赖;
使用npminstallexpress--save命令安装express模块。
运行yarnstart,即可打开预览
工作原理现在来了解这个系统是如何工作的,此应用程序有2个流程需要服务器和客户端之间的严格协调。
上传流程:获取新文件,发送有关文件信息到服务器,服务器返回一个密钥(id),在发送文件块时需要使用该密钥(id),允许它跟踪文件并能够在以后发生中断时恢复它上传。
恢复上传流程:向服务器查询提供的名称和密钥(id)的文件的状态,以便服务器可以响应上传停止的块大小,以便上传可以从该点继续。
还有第四个端点,用于获取所有待处理的文件密钥以恢复上传,以防上传停止并且想在几天后恢复。对于本教程,一旦上传并获取ID,会将其保留在客户端以恢复它们,但是如果关闭浏览器选项卡,ID将丢失,并且将无法恢复。pyramid源码
客户端这里的客户端主要是WEB应用端。这个项目的HTML非常简单,修改文件app/index.html,下面是核心的代码。
<divclass="rowmarketing"><divclass="col-lg-"><labelclass="upload-btn">上传文件<inputtype="file"multipleaccept="video/*"id="file-upload-input"style="display:none"/></label></div></div>这里的重要细节是input属性必须具有multiple属性以允许用户选择多个文件,还可以选择使用accept属性标识允许上传的文件类型。
对于上传文件,通过input的id属性获取元素对象,并为其绑定事件change来监听用户文件的选择。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});下面代码定义了uploadFiles方法:
constuploadFiles=(()=>{ constURL=`http://localhost:/`;constENDPOINTS={ UPLOAD:`${ URL}upload`,UPLOAD_STATUS:`${ URL}/upload-status`,UPLOAD_REQUEST:`${ URL}/upload-request`,};constdefaultOptions={ url:ENDPOINTS.UPLOAD,startingByte:0,fileId:"",onAbort(){ },onProgress(){ },onError(){ },onComplete(){ },};return(files,options=defaultOptions)=>{ //handlefileobjectshere};})();上面的代码返回一个函数,它接受一个文件列表和一个可选的选项对象来处理上传。涉及的三个API端点如下:
upload:传输文件块
upload-status:查询文件上传状态,如果开始上传和停止或有什么东西中断了,迫使选择一个文件再次上传,这将返回关于文件停止的位置、在中断之前上传了多少块的信息。
upload-request:通知服务器要上传的内容,以便服务器设置密钥并在上传开始时跟踪文件。
选项对上传的各个阶段或状态进行回调(中止、进度、错误和完成),起始字节是从要发起上传的文件流的哪个位置开始,文件id是标识文件的一种方法。
文件上传UI/UX处理在服务器上设置上传端点之前,最好在客户机上处理它,因为这将帮助服务器端更有意义。
对于这一部分,需要一些处理UI的东西。这部分可以很容易地用任何UI库或框架来完成,allin源码比如Vue和Angular。
类似于uploadFiles函数,需要与uploadFiles交互并更新UI的uploadAndTrackFiles。uploadAndTrackFiles是一个函数,它获取一个文件列表并调用uploadFiles,然后通过将页面体上的每个文件元素进度指示器附加到progressBox容器中来设置视图。
在内部,它还有所有的回调函数来跟踪和响应每个文件状态。
constuploadAndTrackFiles=(()=>{ constprogressBox=document.createElement("div");letuploader=null;constsetFileElement=(file)=>{ //createfileelementhere};constonProgress=(e,file)=>{ };constonError=(e,file)=>{ };constonAbort=(e,file)=>{ };constonComplete=(e,file)=>{ };return(uploadedFiles)=>{ [...uploadedFiles].forEach(setFileElement);document.body.appendChild(progressBox);uploader=uploadFiles(uploadedFiles,{ onProgress,onError,onAbort,onComplete,});};})();从change事件侦听器中调用uploadAndTrackFiles传递文件列表。
elemFileInput.addEventListener("change",(e)=>{ uploadAndTrackFiles(e.currentTarget.files);e.currentTarget.value="";});现在要做的一件事是清除之后的input的值,这样浏览器就就不会阻止用户添加更多的文件。到目前为止,可以回到uploadFiles函数中处理上传。
文件上传逻辑回到uploadFiles函数,循环遍历文件列表,并调用处理请求初始化的uploadFile。发起请求后,将返回一个对象,返回的对象为公开函数,当使用文件调用该函数集。
constuploadFiles=(()=>{ constURL=`http://localhost:/`;constENDPOINTS={ UPLOAD:`${ URL}upload`,UPLOAD_STATUS:`${ URL}/upload-status`,UPLOAD_REQUEST:`${ URL}/upload-request`,};constdefaultOptions={ url:ENDPOINTS.UPLOAD,startingByte:0,fileId:"",onAbort(){ },onProgress(){ },onError(){ },onComplete(){ },};constfileRequests=newWeakMap();constuploadFile=(file,options)=>{ };constabortFileUpload=(file)=>{ };constretryFileUpload=(file)=>{ };constclearFileUpload=(file)=>{ };constresumeFileUpload=(file)=>{ };return(files,options=defaultOptions)=>{ [...files].forEach((file)=>{ uploadFile(file,{ ...defaultOptions,...options});});return{ abortFileUpload,retryFileUpload,clearFileUpload,resumeFileUpload,};};})();因为可以同时上传多个文件,将定义WeakMap(与Map相比,键名是弱引用,键值可以是任意的,键名所指向的对象可以被垃圾回收)类型的变量fileRequests来跟踪所有文件。
uploadFile函数只是让服务器知道它应该期待一个文件很快被上传,并提供文件名。
constuploadFiles=(()=>{ constURL=`http://localhost:/`;constENDPOINTS={ UPLOAD:`${ URL}upload`,UPLOAD_STATUS:`${ URL}/upload-status`,UPLOAD_REQUEST:`${ URL}/upload-request`,};constdefaultOptions={ url:ENDPOINTS.UPLOAD,startingByte:0,fileId:"",onAbort(){ },onProgress(){ },onError(){ },onComplete(){ },};constfileRequests=newWeakMap();constuploadFile=(file,options)=>{ };constabortFileUpload=(file)=>{ };constretryFileUpload=(file)=>{ };constclearFileUpload=(file)=>{ };constresumeFileUpload=(file)=>{ };constuploadFileChunks=(file,options)=>{ };constuploadFile=(file,options)=>{ returnfetch(ENDPOINTS.UPLOAD_REQUEST,{ method:"POST",headers:{ "Content-Type":"application/json",},body:JSON.stringify({ fileName:file.name,}),}).then((res)=>res.json()).then((res)=>{ options={ ...options,...res};fileRequests.set(file,{ request:null,options});uploadFileChunks(file,options);}).catch((e)=>{ options.onError({ ...e,file});});};return(files,options=defaultOptions)=>{ [...files].forEach((file)=>{ uploadFile(file,{ ...defaultOptions,...options});});return{ abortFileUpload,retryFileUpload,clearFileUpload,resumeFileUpload,};};})();一旦服务器用文件id进行响应,它就会更新函数,lender源码并使用请求选项创建一个文件请求空记录,以备后续重试。一旦这些都设置好了,它就会调用带有文件和更新选项的uploadFileChunks,这些选项将实际处理上传。
在这里,首先初始化表单数据和请求,然后从起始字节点开始对文件进行切片。
文件是一个Blob类型,它让用TypedArray来切片文件字节,这就是如何跟踪从哪一点开始上传文件服务器会负责把文件一块一块地放在一起。
constuploadFileChunks=(file,options)=>{ constformData=newFormData();constreq=newXMLHttpRequest();constchunk=file.slice(options.startingByte);formData.append("chunk",chunk,file.name);formData.append("fileId",options.fileId);req.open("POST",options.url,true);req.setRequestHeader("Content-Range",`bytes=${ options.startingByte}-${ options.startingByte+chunk.size}/${ file.size}`);req.setRequestHeader("X-File-Id",options.fileId);req.onload=(e)=>{ //当请求状态不是时被调用//这将只把视为成功,其他的都是失败if(req.status===){ options.onComplete(e,file);}else{ options.onError(e,file);}};req.upload.onprogress=(e)=>{ constloaded=options.startingByte+e.loaded;options.onProgress({ ...e,loaded,total:file.size,percentage:(loaded*)/file.size,},file);};req.ontimeout=(e)=>options.onError(e,file);req.onabort=(e)=>options.onAbort(e,file);req.onerror=(e)=>options.onError(e,file);fileRequests.get(file).request=req;req.send(formData);};然后,通过提供块的名称(即文件名)和文件id与服务器通信,并通过在将要发送的表单数据中设置这些属性。
当使用XMLHttpRequest时,需要打开一个请求,在这里发起一个POST请求,设置一些头信息。
Content-Range:这是与服务器通信的方式发送的文件字节的范围。该信息对服务器很重要,以便知道如何将文件放回一起并验证请求。
X-File-Id:传递文件id的另一种方式。
一旦设置好请求,就用传递事件和文件Blob的选项回调来映射上传进度事件。
一旦上传完成,就会触发load事件。
当请求接收到更多数据时,就会触发progress事件,从那里可以提取到目前为止上传的总字节数,并计算上传进度百分比。
timeout和error将被视为失败;
当触发中止以暂停上传时,中止将被触发。
接下来要做的就是使用WeakMap中的实际请求更新文件请求并发送表单数据。
文件恢复上传逻辑恢复上传的逻辑要简单得多,这里,所要做的就是使用提供的文件从WeakMap中获取文件请求,并将文件id和名称作为查询参数传递到服务器上查询该文件的上传状态。
有了状态(之前上传的总字节)后,调用uploadFileChunks函数,提供开始上传的字节。
constresumeFileUpload=(file)=>{ constfileReq=fileRequests.get(file);if(fileReq){ returnfetch(`${ ENDPOINTS.UPLOAD_STATUS}?fileName=${ file.name}&fileId=${ fileReq.options.fileId}`).then((res)=>res.json()).then((res)=>{ uploadFileChunks(file,{ ...fileReq.options,startingByte:Number(res.totalChunkUploaded),});}).catch((e)=>{ fileReq.options.onError({ ...e,file});});}};文件重试上传逻辑文件上传过程中可能会出错,也许网络失去了连接,服务器崩溃了等等。
其逻辑与恢复上传逻辑几乎相同,唯一的区别是在catch块上,重新启动上传,以防文件从未开始上传,并且服务器不知道它。
constretryFileUpload=(file)=>{ constfileReq=fileRequests.get(file);if(fileReq){ //trytogetthestatusjustincaseitfailedmiduploadreturnfetch(`${ ENDPOINTS.UPLOAD_STATUS}?fileName=${ file.name}&fileId=${ fileReq.options.fileId}`).then((res)=>res.json()).then((res)=>{ //ifuploadedwecontinueuploadFileChunks(file,{ ...fileReq.options,startingByte:Number(res.totalChunkUploaded),});}).catch(()=>{ //ifneveruploadedwestartuploadFileChunks(file,fileReq.options);});}};文件暂停上传逻辑要暂停当前上传的文件,需要中止请求。除了抓取文件上传进度,中止请求的能力是使用XMLHttpRequest的第二个原因。
当使用文件调用这个函数时,从WeakMap中获取请求,并使用请求调用abort函数来停止上传。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});0这将简单地停止向服务器发送块,然后可以通过调用resumeFileUpload函数继续上传。
文件清除逻辑在上传完成时或之后清除或取消上传,它只会中止请求并清除它,执行清除操作后将无法继续恢复上传。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});1上传进度在uploadAndTrackFiles函数,可以定义进度框的内部HTML,它将显示当前有多少文件正在上传,并提供一个上传进度恢复表,告诉有多少文件失败了、成功了、暂停了等等。
还有一个最大化按钮,当点击将展开或折叠上传程序。紧接着,有一个总的文件上传进度条和一个容器,将放置每个文件指示元素和单独的状态和控件。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});2一旦接收到文件Blobs,就可以使用它来创建文件元素,方法是调用setFileElement,它创建一个包含文件名、进度条、百分比以及用于处理文件上传的控制按钮的div。
还需要跟踪各个文件元素,以便稍后可以使用文件Blob引用它们。需要跟踪它的大小、状态、百分比和上传的块大小。所有这些细节将用于呈现元素进度细节并相应地更新视图。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});3一旦使用了这些元素,就获取了它的控制按钮的引用,并附加了从上传器调用公开方法的单击事件,以控制传递文件的内容。
当清除元素时,需要通过调用updateProgressBox函数来更新进度框元素。这是更新所有上传文件的整体细节的函数。比如所有失败、成功、暂停和上传文件。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});4还需要根据文件上传进度事件更新各个文件元素,它只是更新进度条百分比和状态文本消息,并根据文件上传状态切换可见或不可见的按钮。
constelemFileInput=document.getElementById("file-upload-input");elemFileInput.addEventListener("change",(e)=>{ //handlefilehere});5现在有了基于上传事件更新元素的函数,需要相应地处理所有文件上传事件。
因此,每当进程事件调用onProgress回调函数时,将使用该文件从setFileElement函数中设置的文件中获取渲染的文件对象,并使用事件细节更新其状态、百分比和块大小,然后调用updateFileElement函数。
constelemFileInput=document.getElementByIdMidwayJS 全栈开发(一)技术选型
本系列文章旨在分享学习 NodeJS 服务端开发,特别是使用 MidwayJS 的一些经验和实践探索。对全栈开发有兴趣的前端同学,本文也欢迎你一起交流学习。
NodeJS 是一门非常适合后端入门的JavaScript语言,具有JavaScript基础的同学能快速上手,且能使用强大的NPM包生态,帮助快速实现全栈开发转型。对于全栈开发的考量,我们还需要考虑实际应用场景,判断NodeJS是否适合当前场景。
通过调研,NodeJS相比传统Java有其优势,如开发效率高、支持异步编程等。但也有不足,如性能问题、单线程限制等。NodeJS在后端领域有其适用场景,作为全栈工程师,学习它能提升全局视野,解决特定场景问题。
在后端框架选择上,常见的NodeJS框架包括Express、Koa、Nest和Midway。Express作为早期框架,流行度高,内置常用中间件,使用Error-First模式。Koa2基于Async Functions实现,关注性能与定制扩展性。NestJS是成体系的NodeJS框架,围绕三个核心元素开发REST API。Midway则是阿里推出的基于渐进式理念的框架,底层依赖Koa,支持TS、装饰器路由和依赖注入等特性,且在路由声明上更为便捷。
NodeJS Web框架大致分为两类,一类是Express、Koa等,大而全,功能丰富;另一类是Nest、Midway等,小而精,专注于特定功能,同时具有良好的扩展性。选择时,可考虑项目需求、团队熟悉度及未来趋势。
在选择NestJS或MidwayJS时,两者能力与使用方式大同小异,底层依赖不同(NestJS依赖Express,Midway依赖Koa)。熟悉Koa的同学可尝试MidwayJS,而熟悉Express的同学则可考虑NestJS。本文作者出于学习目的,选择了MidwayJS,原因在于其较新的设计及与NestJS的对比,同时也考虑了国产框架的社区支持。
本文总结了NodeJS的优劣、适用场景,以及基于NodeJS的流行Web框架对比,旨在为Web服务端开发提供参考和选择依据。希望对全栈开发的学习者有所帮助。