# image-CloudDisk **Repository Path**: onlypluto/image-cloud-disk ## Basic Information - **Project Name**: image-CloudDisk - **Description**: image-CloudDisk是一个Vue练手项目,由个人开发者负责维护,旨在向前端初学者和Vue使用者提供真实的项目开发经验。 - **Primary Language**: JavaScript - **License**: MIT - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 105 - **Forks**: 23 - **Created**: 2021-06-08 - **Last Updated**: 2025-05-24 ## Categories & Tags **Categories**: Uncategorized **Tags**: Vue ## README # 项目详细文档 --- # 项目信息 ## 1.这是一个什么项目? image-CloudDisk 是一个 Vue 练手项目,由个人开发者负责维护,旨在向前端初学者和 Vue 使用者提供**真实的项目开发经验**。 在客户端,我们提供了完整的源代码,各位可以结合示例代码,开发自己的客户端。 在服务器端,我们提供大量的真实 API,并附带翔实的接口文档。 ![项目展示](https://alonepluto.info/api/cloud/i1.jpg) ![项目展示](https://alonepluto.info/api/cloud/i2.jpg) ## 2.我能学到什么? iamge-CloudDisk 是一个照片和视频的云盘管理程序,通过学习开发这一前端项目,我们相信你会对如下内容有深入理解: - Vue 组件通信 - Vue 生命周期 - Vue 组件化开发 - 事件总线与状态管理 - 异步请求与事件循环 - promise async/await - 前端处理二进制数据 - 大文件分片上传 - 前端分页与后端分页 - 缩略图与 canvas - 网络通信与请求头 - ... ## 3.配套的学习资源 - 深入浅出 vuetify(已完结),视频地址 https://www.zhihu.com/zvideo/1437895429194846208 ![项目展示](https://alonepluto.info/api/cloud/vuetify.jpeg) - 项目源码视频讲解(更新中),视频地址https://www.zhihu.com/zvideo/1474148791100424193 ![项目展示](https://alonepluto.info/api/cloud/封皮.jpeg) # 接口文档 ## 一、调用前必读 ### 1. 项目技术栈 - 前端框架:Vue2.x - 后端框架:Koa - UI 库:Vuetify https://vuetifyjs.com/zh-Hans/getting-started/installation/ - 图标库:Material Design https://material.iconhelper.cn/ - 数据库:mysql ### 2.仓库管理 欢迎向仓库 issue 你的想法、建议和代码 如果你愿意参与到这个项目中,我们非常欢迎 扫码联系项目维护者: 在知乎联系我: ![zhihu](https://alonepluto.info/api/cloud/zhihu.jpeg) 在微信联系我: ![zhihu](https://alonepluto.info/api/cloud/weixin.jpeg) ### 3.捐助 请项目维护者喝杯咖啡 ![zhihu](https://alonepluto.info/api/cloud/ww.jpeg) ## 二、基本配置 ### 1. baseURL 在这里,我们提供一个基础的网络请求地址,接下来的所有 url 都以此地址为 baseURL ```javascript baseURL: "https://alonepluto.info/api/cloud/v1" ``` ### 2.跨域问题 当然,这是一个绕不开的问题,跨域可以在后端解决,也可以在前端解决。 在**1.1.7 版本**之前,项目的跨域是在后端解决,之后为了加强对 Vue 的练习,我们果断把这个问题抛给了前端 你可以通过配置 vue.config.js 中的 devServer.proxy 来解决跨域问题 ```javascript //vue.config.js module.exports = { // 开发环境代理服务器 devServer: { proxy: { "/api": { target: "https://alonepluto.info/api/cloud/v1", changeOrigin: true, pathRewrite: { "^/api": "" } } } } } ``` ### 3. SecretKey 为了防止接口被恶意调用,节约服务器资源,调用**所有接口均需向服务器传递 SecretKey(/test 接口除外)**,你需要在发送网络请求时,设置自定义请求头 SecretKey,下面以 axios 为例: ```javascript axios({ method:'get' url:'/login', //设置请求头,建议使用拦截器自动添加请求头 headers:{ SecretKey:'e8feJD8fje9jfkellppzw80eYmTTs' } }) ``` 项目封装了 axios,并针对开发环境配置了环境变量,你可以用最新的 SecretKey 替换项目根目录下.env.development 文件中的 VUE_APP_SECRET_KEY,以轻松完成对 SecretKey 的引入 ```javascript //.env.development NODE_ENV = development VUE_APP_BASE_URL = /api VUE_APP_SECRET_KEY = XG9RVD8F9EGH //替换 ``` **你可以通过知乎或者微信向项目维护者索要最新的 SecretKey** ### 3. token 前后端通信通过 token 保持登录状态 登录成功后,服务器端会向前端返回一个 token 值,后续前端发起网络请求时,均需向服务器传递 token(/test 接口除外),以验证身份。无 token 或 token 过期,则网络请求失败。 服务器端拦截网络请求的请求头,通过 Authorization 字段获取 token 值,因此,前端发起网络请求时,需要设置请求头,下面以 axios 为例: ```javascript axios({ method:'post' url:'/edit', //设置请求头,建议使用拦截器自动添加请求头 headers:{ Authorization:'gkrlkoxg5ffFE89vjke77HJK' } }) ``` ### 4.静态资源的下载 在客户端,点击一个图片或视频链接,可直接下载资源,而不是在一个新窗口打开资源,这极大简化了开发者从服务器下载静态资源的步骤,开发者无需再编写额外的业务代码 ```javascript //客户端 //将资源url赋值给a标签,点击后可直接下载 const a = document.createElement("a") a.setAttribute("href", this.file.file_src) a.click() ``` ```javascript //服务器端 //响应类型为通用文件类型 ctx.set("content-type", "application/octet-stream") //要求浏览器下载文件,而不是在浏览器中进行展示 ctx.set("Content-Disposition", `attachment;filename=${ctx.url.split("/").pop()}`) ``` 实现这一功能的关键在于服务器端设置了特殊的响应头,它强制要求浏览器直接下载资源,而不需要打开它 **如果你不了解 nodejs 可忽略这部分内容,你只需要知道,请求服务器端静态资源,会直接下载该资源** ### 5.网络测试接口 服务器暴露了一个网络测试接口,接口地址:'/test',请求方法:'get',如果请求 test 接口返回如下内容,说明服务器工作正常 ```javascript { code:825, message:'网络通信正常' } ``` - test 接口通常用于前后端通信调试,无实际业务意义 ## 三、API ### A. 登录接口 #### 1.登录 - 说明:用于用户登录 - 接口地址:'/login/login' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------------- | -------- | ------ | -------- | ---------------------- | | user_name | 用户名 | String | 是 | user_name:'admin' | | user_password | 用户密码 | String | 是 | user_password:'123456' | - 返回值 ```javascript { //服务器自定义状态码 code:802, //响应结果 message:'登录成功', //响应数据 data:{ //用户详细信息 user:{}, //令牌,用于后续请求服务器数据时验证身份 token:'e8Vufe8JFKLEV9989je8Vhkf8v', //用户收藏夹列表 folders:[] } } ``` #### 2.注册 - 说明:用于新用户注册 - 接口地址:'/login/signin' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------------- | ------------------------------------ | ------ | -------- | ----------- | | user_name | 用户名 | String | 是 | - | | user_password | 用户密码 | String | 是 | - | | user_question | 用户密保问题 | String | 否 | - | | user_answer | 密保问题答案 | String | 否 | - | | mock | **服务器虚拟 10 天的数据,建议使用** | String | 否 | mock:'mock' | - 返回值 ```javascript { //服务器自定义状态码 code:820, //响应结果 message:'注册成功', } ``` - 提示: 1. 建议用户密码使用 MD5 加密后再传输 2. 注册用户名必须唯一 3. 服务器会对用户名进行验证,对于长度错误、使用非法字符或者重名的用户名会拒绝注册,因此,发起网络请求前,开发者最好先行对用户名进行验证 4. 建议开发者在开发注册功能时设置密保问题和答案,否则密码重置功能不可用 #### 3.校验用户名 - 说明:用于新用户注册时,请求服务器验证用户名是否已存在 - 接口地址:'/login/checkname' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | ---------- | ------ | -------- | ---- | | user_name | 用户注册名 | String | 是 | - | - 返回值: ```javascript { //服务器自定义状态码 code:810, //响应结果 message:'验证成功,用户名可用', } ``` - 提示: 为了良好的用户体验,前端最好在发起注册请求前,先行验证用户输入的注册名是否在服务器已存在 你可以在用户名输入框失去焦点时请求此接口 #### 4.重置密码 - 说明:用于用户忘记登录密码时,重置密码 - 接口地址:'/login/resetpassword' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------------- | ---------- | ------ | -------- | ---- | | user_name | 用户名 | String | 是 | - | | user_password | 用户新密码 | String | 是 | - | - 返回值: ```javascript { //服务器自定义状态码 code:808, //响应结果 message:'操作成功', } ``` #### 5.获取密保问题列表 - 说明:获取由服务器提供的密保问题列表,当然,你也可以自己设置一个密保问题,不过这不是一个好的选择 - 接口地址:'/login/getquestions', - 请求方法:'get' - 参数:无 - 返回值: ```javascript { //服务器自定义状态码 code:800, //响应结果 message:'查询成功,返回结果', //密保问题列表 data:[ "你最想去的星球?", "你最喜欢的一本书?", "你最讨厌的水果?", "你最难忘的人?" ] } ``` #### 6.获取用户密保问题和答案 - 说明:获取用户预先设置的密保问题和答案,用以重置密码时验证用户身份 - 接口地址:'/login/getuserquestion' - 请求方法:'get', - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | ------ | ------ | -------- | ---- | | user_name | 用户名 | String | 是 | - | - 返回值: ```javascript { //服务器自定义状态码 code:800, //响应结果 message:'查询成功,返回结果', //密保问题列表 data:{ user_question:'你最讨厌的水果?', user_answer:'榴莲' } } ``` ### B. 数据接口 #### 1.请求时间戳分页数据 - 说明:请求时间戳功能的数据 - 接口地址:'/data/getTimelineDataLimit' - 请求方法:'get' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------ | ---------------- | ------ | -------- | ---- | | page | 请求第几页数据 | Number | 是 | 0 | | limit | 每页显示几条数据 | Number | 是 | | - 返回值: ```javascript { code:800, message:'查询成功,返回结果', data:[ [{...},{...},{...}], [{...}], [{...},{...}], ... ] } ``` - 提示: 返回的数据为一个二维数组,每个数组中储存相同日期上传的所有图片和视频,数组元素按时间降序排列 #### 2.请求时间戳全部数据 - 说明:以时间戳的方法获取全部数据 - 接口地址:'/data/getTimelineData' - 请求方法:'get' - 参数:无 - 返回值: ```javascript { code:800, message:'查询成功,返回结果', data:[ [{...},{...},{...}], [{...}], [{...},{...}], ... ] } ``` #### 3.请求全部图片数据 - 说明:一次性请求用户上传的所有图片格式的数据 - 接口地址:'/data/getImageData' - 请求方法:'get' - 参数:无 - 返回值: ```javascript { code:800, message:'查询成功,返回结果', data:[ {...}, {...}, ... ] } ``` - 提示: 返回的是一个一维数组,所有的数据按时间降序排列 #### 4.请求全部视频数据 - 说明:一次性请求用户上传的所有视频格式的数据 - 接口地址:'/data/getvideoData' - 请求方法:'get' - 参数:无 - 返回值: ```javascript { code:800, message:'查询成功,返回结果', data:[ {...}, {...}, ... ] } ``` - 提示: 返回的是一个一维数组,所有的数据按时间降序排列 #### 5.分页请求全部数据 - 说明:灵活地按照资源类型分页请求数据 - 接口地址:'/data/getDataLimit' - 请求方法:'get', - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------ | ---------- | ------ | -------- | -------------------------------------------------------- | | type | 资源类型 | String | 否 | 默认值 all,即同时请求图片和视频,可选值为 image 和 video | | page | 分页 | Number | 是 | - | | limit | 每页数据量 | Number | 是 | - | #### 6.请求特定日期的数据 - 说明:请求特定日期的数据 - 接口地址:'/data/getDateData' - 请求方法:'get' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | -------- | ------ | -------- | --------------------------- | | date_list | 日期数组 | Array | 是 | ['2021/06/02','2021/06/03'] | - 返回值 ```javascript { code:800, message:'查询成功,返回结果', data:[ {'2021/06/02':[{...},{...}]}, {'2021/06/03':[{...}]} ] } ``` - 提示: 响应结果为数组,数组元素为以查询日期为键的键值对 #### 7.数据统计 - 说明:获取用户上传的图片的数量、视频的数量、上传文件的总大小 - 接口地址:'/data/getCounter' - 请求方法:'get' - 参数:无 - 返回值: ```javascript { code:800, message:'查询成功,返回结果', data:{ image:20, video:15, size:9866234 } } ``` #### 8.创建收藏夹 - 说明: - 接口地址:'/data/foundfolder' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ----------- | ---------- | ------ | -------- | ---- | | folder_name | 收藏夹名称 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 9.删除收藏夹 - 说明:删除用户收藏夹 - 接口地址:'/data/deleteFolder' - 请求方法:'delete' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ----------- | ---------- | ------ | -------- | ---- | | folder_name | 收藏夹名称 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 10.将文件添加到收藏夹 - 说明:将图片或视频添加到用户已创建的收藏夹 - 接口地址:'/data/addFolder' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ----------- | ---------------- | ------ | -------- | ---- | | file_id | 文件 ID | String | 是 | - | | file_folder | 添加到哪个收藏夹 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 11.删除文件 - 说明: - 接口地址:'/data/deleteFile' - 请求方法:'delete' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------- | ------- | ------ | -------- | ---- | | file_id | 文件 ID | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 12.文件重命名 - 说明:删除单个文件 - 接口地址:'/data/renameFile' - 请求方法:'put' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | ---------- | ------ | -------- | ---- | | file_id | 文件 ID | String | 是 | - | | file_name | 文件新名称 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 13.批量删除文件 - 说明:批量删除文件 - 接口地址:'/data/bulkDelete' - 请求方法:'delete' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | -------------------- | ------ | -------- | ---- | | file_list | 要删除的文件 ID 数组 | Array | 是 | - | - 返回值: ```javascript { code:814, message:'批量删除成功', //响应数据为成功删除的文件ID列表 data:[ '1658938495', '1690697063' ] } ``` #### 14.请求收藏夹数据 - 说明:统计同一个收藏夹下的图片和视频的数量 - 接口地址:'/data/getFolderData' - 请求方法:'get' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------ | ---------- | ------ | -------- | ---- | | folder | 收藏夹名称 | String | 是 | - | - 返回值: ```javascript { code:800, message:'查询成功,返回结果', //返回统计结果和明细 data:{ counter:{ image:4, video:2 }, result:[ {...}, {...}, ... ] } } ``` #### 15.编辑备忘 - 说明:编辑备忘内容 - 接口地址:'/data/editMemo' - 请求方法:'put' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | -------- | ------ | -------- | ---- | | file_id | 文件 ID | String | 是 | - | | file_memo | 备忘内容 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` ### C. 上传接口 #### 1.上传 - 说明:文件上传接口 - 接口地址:'/upload/upload' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------ | ---------------- | ------ | -------- | ---- | | hash | 文件唯一性标识符 | String | 是 | - | | index | 文件切片索引 | String | 是 | - | | chunk | 文件切片 | Binary | 是 | - | - 返回值: ```javascript { //服务器自定义状态码 code:812, //响应结果 message:'上传成功', } ``` - 提示: 1. 切片大小不可超过 2MB,否则服务器拒绝上传 2. 请使用 FormData 上传文件 #### 2.合并 - 说明:切片全部上传后,请求此接口,可将切片重新合并成图像 - 接口地址:'/upload/completed' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | --------- | --------------------------- | ------ | -------- | ----------------------------- | | hash | 文件唯一性标识符 | String | 是 | | | chunkNum | 切片总数量 | Number | 是 | | | file_name | 文件名称 | String | 是 | i-1.jpg | | file_size | 文件大小 | Number | 是 | | | file_type | 文件类型 | String | 是 | image/jpeg、video/mp4 | | file_mini | 文件缩略图,base64 编码格式 | String | 是 | data:image/png;base64,iVB.... | - 返回值: ```javascript { code:808, messge:'操作成功', data:{ //文件ID file_id:165689452888, //文件名 file_name:'i-2.jpg', //文件服务器地址 file_src:'http://127.0.0.1:3000/static/162656911656/image/origin/i-2.jpg', //缩略图服务器地址 file_mini:'http://127.0.0.1:3000/static/162656911656/image/mini/e5feb634f48.png', //文件所有者ID file_owner:162656911656, //文件上传年份 file_year:2021, //文件上传月份 file_month:05, //文件上传日期 file_day:12, //文件上传完整时间 file_time:2021/05/12, //文件大小 file_size:85625, //文件类型 file_type:'image/jpg' } } ``` ### D. 用户接口 #### 1.上传头像 - 说明:将一张头像图片上传到服务器 - 接口地址:'/user/uploadAvatar' - 请求方法:'post' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ----------- | ------------ | ------ | -------- | ---- | | user_avatar | 用户头像图片 | File | 是 | - | - 返回值: ```javascript { code:812, message:'上传成功' } ``` - 提示: 1. 头像大小不可超过 20KB,否则服务器拒绝上传 #### 2.修改密码 - 说明:用户修改登录密码 - 接口地址:'/user/changePassword' - 请求方法:'put' - 参数: | 参数名 | 说明 | 值类型 | 是否必选 | 示例 | | ------------- | ---------- | ------ | -------- | ---- | | user_password | 用户新密码 | String | 是 | - | - 返回值: ```javascript { code:808, message:'操作成功' } ``` #### 3.编辑密保问题 - 说明:编辑用户密保问题和答案 - 接口地址:'/user/editQuestion' - 请求方法:'post' - 参数: | 参数值 | 说明 | 值类型 | 是否必选 | 示例 | | ------------- | ------------ | ------ | -------- | ---- | | user_question | 用户密保问题 | String | 是 | - | | user_answer | 密保问题答案 | String | 是 | - | - 返回值: ```javascript { code: 808 message: "操作成功" } ```