源码解析:S3DirectUpload 前端 CoffeeScript 是如何工作的?
【免费下载链接】s3_direct_uploadDirect Upload to Amazon S3 With CORS项目地址: https://gitcode.com/gh_mirrors/s3/s3_direct_upload
S3DirectUpload 是一个借助 CORS 实现直接上传到 Amazon S3的 Rails gem,它的灵魂是一段由CoffeeScript编写的前端 jQuery 插件源码。本文将逐行拆解这份源码,带你搞懂"浏览器绕过服务器、把文件直传 S3"的完整机制,包括上传流程、进度条刷新、key 占位符替换等核心细节,非常适合想看懂 S3 直传原理的新手。
一、先认识主角:前端插件源码长什么样?
整个前端逻辑全部集中在一个文件里:app/assets/javascripts/s3_direct_upload.js.coffee。它通过#= require jquery-fileupload/basic引入了 jQuery File Upload 插件,然后对外暴露一个标准的 jQuery 插件方法:
$.fn.S3Uploader = (options) ->这意味着你在页面里可以这样调用:
$("#s3-uploader").S3Uploader()插件还贴心地做了多元素支持:如果选择器命中了多个表单(@length > 1),它会用@each逐个初始化,互不干扰。
📁 相关文件:
app/assets/javascripts/s3_direct_upload.js.coffee、app/assets/stylesheets/s3_direct_upload_progress_bars.css.scss
二、上传前的准备:Ruby 端如何"发号施令"?
直传 S3 的关键前提是安全策略。服务端不负责传输文件,但必须签发"准入凭证"。lib/s3_direct_upload/form_helper.rb中的s3_uploader_form会为表单生成一批隐藏字段,包括:
| 字段 | 作用 |
|---|---|
key | 文件在 S3 上的存储路径模板 |
policy | Base64 编码的上传策略 JSON |
signature | 用密钥对 policy 做的 HMAC-SHA1 签名 |
AWSAccessKeyId | AWS 访问密钥 ID |
acl | 文件权限(默认 public-read) |
success_action_status | 上传成功后返回 201 状态码 |
前端源码正是读取这些隐藏字段,拼出符合 S3 要求的 POST 请求。formData回调里的这段代码就是核心:
key = $uploadForm.data("key") .replace('{timestamp}', new Date().getTime()) .replace('{unique_id}', @files[0].unique_id)三、核心流程拆解:一次文件上传的生命周期
jQuery File Upload 会按顺序触发几个回调,S3DirectUpload 的源码把它们组织成了一条清晰的流水线:
| 回调 | 触发时机 | 源码做了什么 |
|---|---|---|
add | 选中文件 | 生成unique_id、执行before_add校验、渲染进度条 |
start | 开始上传 | 触发s3_uploads_start事件 |
progress | 上传中 | 按已加载比例刷新进度条宽度 |
done | 上传成功 | 解析 S3 返回的 URL、可选回传服务器 |
fail | 上传失败 | 组装错误信息、触发s3_upload_failed |
两个值得注意的细节:
unique_id如何生成?源码用Math.random().toString(36).substr(2,16)生成一段随机字符串,专门用来避免同名文件(比如手机里全是image.jpg)在 S3 上互相覆盖。before_add是拦截器。你可以传入一个返回布尔值的函数,返回false就取消该文件上传,常用于文件名校验。
四、进度条是如何实时刷新的?
进度条的秘密藏在progress回调里,代码极简:
progress: (e, data) -> if data.context progress = parseInt(data.loaded / data.total * 100, 10) data.context.find('.bar').css('width', progress + '%')每次浏览器报告上传进度,就把.bar元素的宽度改成对应的百分比。进度条外观由s3_direct_upload_progress_bars.css.scss定义:灰色边框 + 绿色填充条,400px 宽,上传完成后默认自动移除(可通过remove_completed_progress_bar: false保留)。
进度条模板来自页面里的template-upload脚本标签,源码会用它渲染出带unique_id的进度条容器,再插入到表单或progress_bar_target指定的位置。
五、key 占位符替换机制:文件最终存到哪里?
S3 上的存储路径由key模板决定,默认长这样:
uploads/{timestamp}-{unique_id}-随机值/${filename}前端源码在formData回调里依次替换四个占位符:
{timestamp}→ 当前毫秒时间戳,保证唯一性{unique_id}→ 随机字符串,防止同名冲突{cleaned_filename}→ 清洗后的文件名{extension}→ 文件扩展名
其中cleaned_filename函数负责把文件名里的空格换成下划线、剔除特殊字符:
cleaned_filename = (filename) -> filename.replace(/\s/g, '_').replace(/[^\w.-]/gi, '')💡 顺带一提:源码还专门处理了IE 9 及以下的兼容——它们没有 XHR2/FormData,只能退化为普通表单提交,所以会直接改写隐藏的
key输入框值。
六、上传完成后:content 对象与事件钩子
上传成功后,build_content_object会从 S3 返回的 XML 响应里解析出Location节点,得到文件的完整 URL,并附带文件名、大小、类型等信息:
content.url = $(result).find("Location").text()拿到content后,插件会触发一系列事件钩子,方便你在业务代码里监听:
s3_upload_complete—— 单个文件上传成功(可拿到content.url填入隐藏字段)s3_uploads_complete—— 整批文件全部完成s3_upload_failed—— 上传失败,附带error_thrown错误信息ajax:success等 —— 服务器回调(callback_url)完成后触发
如果配置了callback_url,插件还会自动把 S3 URL 通过 AJAX POST 回 Rails 服务器,实现"上传完立刻通知后端持久化"。
七、公开方法与高级定制
源码最后暴露了三个公开方法,可随时修改行为:
| 方法 | 作用 |
|---|---|
initialize() | 保存 key、初始化上传逻辑(构造时自动调用) |
path(new_path) | 动态修改 S3 存储路径 |
additional_data(data) | 动态追加回传服务器的附加数据 |
其他常用配置项包括path、before_add、progress_bar_target、click_submit_target、allow_multiple_files等,都可以在S3Uploader()调用时通过 options 传入,配置合并逻辑在源码开头用$.extend settings, options完成。
八、总结:一张图看懂直传流程
用户选择文件 │ ▼ add 回调 → 生成 unique_id → before_add 校验 │ ▼ 渲染进度条 → data.submit() 提交到 S3(CORS 直传) │ ▼ progress 回调 → 实时刷新进度条宽度 │ ▼ done 回调 → 解析 XML 得 URL → 可选回调 Rails 服务器 │ ▼ 触发 s3_upload_complete / s3_uploads_complete 事件一句话概括:Ruby 端负责签发策略与签名,CoffeeScript 前端负责执行上传与交互,服务器全程不碰文件内容,这也是"直传"能大幅节省带宽的根本原因。
想亲手调试这份源码?核心逻辑都在app/assets/javascripts/s3_direct_upload.js.coffee这一个文件里,配合lib/s3_direct_upload/form_helper.rb一起阅读,你就能彻底掌握 S3DirectUpload 的前端工作原理了。🚀
【免费下载链接】s3_direct_uploadDirect Upload to Amazon S3 With CORS项目地址: https://gitcode.com/gh_mirrors/s3/s3_direct_upload
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考