Skip to content

Instantly share code, notes, and snippets.

@maddemon
Created May 30, 2013 06:39
Show Gist options
  • Save maddemon/5676087 to your computer and use it in GitHub Desktop.
Save maddemon/5676087 to your computer and use it in GitHub Desktop.
NodeJs实现可续传的上传协议(tus resumable upload protocol)
Tus协议地址http://www.tus.io/protocols/resumable-upload.html,大家可以看看英文原文介绍,我这里也简单介绍下。
这是一个基于http的可续传的协议,主要是不同的HttpMethod和相关的自定义header来实现续传功能。
具体的Method和header介绍:
1、HEAD
head请求是获取文件已上传的状态,服务端返回该文件哪些范围已写入,哪些没写入。
(PS,我刚发现协议已经变了,以前不是offset)
但是我这个实现和tus也略有不同,我支持一个文件可以多线程同时上传,所以不能完全追寻他的协议。
客户端接收到的header例子:
range:bytes=0-9,20-29
意思表名0-9和20-29这俩个块已被写入,所以续传的时候不需要上传了。
如果接收到的是 range:bytes=0-1023(假如文件就1024长度,那么说明文件已上传完毕)
2、POST
post请求是用于创建文件,header里必须要有声明文件的大小,例如
content-range:*/1023 这说明文件的大小是1024。
返回结果:告诉客户端这个文件的地址,好让客户端知道往什么地址PUT数据。例如:
Location: http://tus.example.org/files/24e533e02ec3bc40c387f1a0e460e216
3、PUT
put请求是上传文件用的,如果想实现续传,那么就要把文件拆分为N个小块,按块上传,这样如果断掉,下次可以通过HEAD请求看到哪些块已经上传。
4、GET
get请求就是下载文件,这里不必多说了。
协议大致如此,只是相对于tus原本的协议有所变化(可以多块同时上传,所以并非是顺序上传,在Head请求的返回自然有所变化)。
协议实现思路:
客户端分块上传,服务端接收到每个块之后存储到服务端。如何记住哪些块被上传了呢?
我的实现思路是需要一个metadata的辅助元数据文件来继续这个文件的上传状态、文件大小、文件原名称等等。
现在是用文件存储的metadata,这只是一个demo,如果是真正使用,我想用数据库更合适一些。
我走的弯路:
第一次:
如何实现块能够写入到目标文件里?这个问题也和以前的同事交流过,当时没有找到解决办法。除非顺序写入然后用append的模式。
但是客户端上传确实多线程非顺序上传,服务端该如何保证顺序?
最后的解决办法是把客户端上传的每一个块保存为单个小文件,每次上传完一个块之后判断是不是所有的块上传完毕,如果上传完毕做一次合并。
判断文件块是否写完,我就要遍历每一个小文件,判断他们是否连续。由于nodejs的文件操作需要异步,所以实现起来需要写各种递归,异常痛苦,大家可以看我的GitHub提交的历史版本,最老的版本里面有很多递归。代码非常丑陋,而且难以维护。
第二次:
为了减少递归,我使用global全局缓存,而且限定了文件上传的块的大小必须一样,没上传完一个块,我就把他存储到global缓存里。这样就不需要遍历小文件,减少了很多异步操作,代码清晰了很多。
第三次:
小文件合并的方案虽然可行,但毕竟感觉有些愚蠢,如果是特别大的文件,那可以想象合并和遍历都很慢,管理也不方便。所以如果能解决Nodejs的按offset写入stream的问题就可以解决。我又翻了下Nodejs的文档,发现以前使用的都是w和w+的打开方式,这种打开方式会清空文件的内容,而r和r+不会。
于是我尝试着修改了代码,由于是并发写入,实际上文件写入是不能并发的,所以需要加锁,当块写入的时候判断是否锁住就可以了。
这一下去掉了好多的代码,代码更加的明亮起来。
为什么用NodeJS? 因为这是一个面试题的作业,所以必须用Nodejs。
我在FileMetadata初始化是依然用的fs.readSync,我以前同事告诉我nodejs不可以使用sync方法,这样会hold住整个进程。这是现实太恐怖了。
所以我个人觉得nodejs的弊端也很明显,对代码复杂度的增加显而易见。 至于Javascript的非阻塞的先天优势,其他的语言也可以做到。那为什么还用Nodejs呢?而且Javascript的语法比较弱,很多实现都比较困难。我对nodejs没有太多好感。
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment