u1jodi1q 发表于 2024-10-31 23:01:56

详解用户照片上传流程


    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">近期</span>在<span style="color: black;">研发</span>新版博客网站时,有几个页面需要<span style="color: black;">运用</span><span style="color: black;">照片</span>上传功能。<span style="color: black;">全部</span>项<span style="color: black;">日前</span>端基于vue3的element-plue和vue-cropper组件库封装一个<span style="color: black;">照片</span>上传组件,后端使Django REST framework<span style="color: black;">研发</span>api接口,存储<span style="color: black;">运用</span>七牛对象存储,以及腾讯CDN加速,总结了完整的前后端代码以及运维配置,以供<span style="color: black;">大众</span>参考。</p>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">1、</span>流程与思路分析</h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 整体流程图</strong></p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/f7ff8770aa1e4c90b5d4c38958c39148~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=HA5hp9WNt%2F9WRyNGxvS%2BgyGzLWU%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 流程分析</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">用户<span style="color: black;">照片</span>上传以及<span style="color: black;">表示</span><span style="color: black;">全部</span>过程可分为以下几个<span style="color: black;">周期</span>:</p>页面加载<span style="color: black;">周期</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">当用户<span style="color: black;">拜访</span><span style="color: black;">https://www.cuiliangblog.cn/applyLink</span>时,前端nginx服务器接收请求(<span style="color: black;">倘若</span>配置了CDN,DNS会智能解析到CDN节点处理请求),返回页面数据给用户,浏览器加载并<span style="color: black;">表示</span>页面</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/d346dbc4763f420faecd6b111d696933~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=fJXPsBNFS6KzpQ%2BS%2FH%2BrerPPdQQ%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/e171da552c5e40aaa3f62646d46a91d2~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=ylcBUjgTs%2FSfIkbMlW6kHhbE%2F7o%3D" style="width: 50%; margin-bottom: 20px;"></div>用户上传<span style="color: black;">照片</span><span style="color: black;">周期</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">用户<span style="color: black;">选取</span>好本地<span style="color: black;">照片</span>文件,点击<span style="color: black;">起始</span>上传操作后,浏览器向后端API接口发送请求,获取此次上传操作的token</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">API后端接收到请求后,<span style="color: black;">运用</span>七牛SDK请求七牛云存储服务,获取上传token后将token返回给客户端</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/b89fd0087a674530928f227f70fb273a~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=AZifbieUODD78BnoIt0R3tCl6sk%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/83ada4b1797f430a8c4dfa41b2b1322c~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=XI8a6MEIUqPT9zGylKgVXPL3w3k%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">客户端<span style="color: black;">运用</span>token上传文件至七牛对象存储服务,上传成功后,七牛存储返回客户端资源的URL<span style="color: black;">位置</span>给客户端</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/fd0bfbc0a53b463d8674877c603db0b8~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=l8QpZDrq09b6Jx1TR76O8%2BKkPqc%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/e57d6aba8757485b902919545ecbb7fb~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=A%2Fkp2UxCeHzWF%2F%2B2hCjvdpAlBWE%3D" style="width: 50%; margin-bottom: 20px;"></div>浏览器请求<span style="color: black;">照片</span>资源<span style="color: black;">周期</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">客户端<span style="color: black;">按照</span><span style="color: black;">照片</span>URL<span style="color: black;">位置</span>请求CDN服务,CDN节点<span style="color: black;">发掘</span><span style="color: black;">无</span>找到资源后回源至七牛对象存储服务,获取文件资源成功后缓存至CDN并返回给客户端</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/03194f11d45642aca0b5fabced37a783~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=6APLGr7%2F0laez8Q125mSxgu278I%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/87f37d41430f4d3b970ab409d96a31cf~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=SwHJVNgdUy5mkxB6iRx4y9mbCoQ%3D" style="width: 50%; margin-bottom: 20px;"></div>用户提交表单<span style="color: black;">周期</span>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">用户提交表单,表单内容中<span style="color: black;">包括</span>资源的URL<span style="color: black;">位置</span>请求后端API接口</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">后端API接口<span style="color: black;">保留</span><span style="color: black;">照片</span>资源URL<span style="color: black;">位置</span></p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/40e19cf8cf234a7980eab30961a6b532~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=49x%2BbxQ7TxIZmA2UMu%2BNxCccchY%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p26-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/78880eefd22f46aa8a8e7d081e590868~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=5QLpLpSAE50REheeoQrhQkRvpl4%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">3. <span style="color: black;">研发</span><span style="color: black;">需要</span>分析</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">本案例<span style="color: black;">运用</span>如今最流行的前后端分离<span style="color: black;">研发</span>模式。</p>前端<span style="color: black;">运用</span>vue3<span style="color: black;">研发</span>,<span style="color: black;">重点</span>实现用户<span style="color: black;">选取</span>本地<span style="color: black;">照片</span>后裁剪成文件blob,以及将文件流和token直接请求对象存储服务,实现文件上传两个功能。后端使python<span style="color: black;">研发</span>,借助Django REST framework框架<span style="color: black;">研发</span>api接口。安装七牛对象存储的sdk,<span style="color: black;">经过</span>请求七牛服务,获取<span style="color: black;">这次</span>操作的token,并返回给前端。<p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">4. 运维配置分析</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">本案例<span style="color: black;">运用</span>主流的企业网站项目配置,<span style="color: black;">运用</span>公有云的OSS对象存储服务、CDN内容分发网络以及DNS域名解析。此处<span style="color: black;">选择</span>七牛云对象存储和腾讯云CDN以及阿里云的域名解析,其他公有云厂商<span style="color: black;">制品</span>名<span style="color: black;">叫作</span>和配置项可能略有差异,但基本原理都是<span style="color: black;">同样</span>的,操作<span style="color: black;">过程</span><span style="color: black;">亦</span>并无差别。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">无</span>注册的小伙伴们<span style="color: black;">能够</span><span style="color: black;">运用</span>以下链接进行注册</p>七牛云:<span style="color: black;">https://s.qiniu.com/VVfMnq</span>阿里云:<span style="color: black;">https://www.aliyun.com/1111/new?userCode=gs1gd34d</span>腾讯云:<span style="color: black;">https://curl.qcloud.com/yXdJdtu9</span>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">2、</span>对象存储配置</h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">此处<span style="color: black;">运用</span>七牛对象存储,有10G免费空间,<span style="color: black;">针对</span><span style="color: black;">通常</span>小业务场景完全够用。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 创建云存储空间</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">登录七牛云——&gt;进入<span style="color: black;">掌控</span>台——&gt;点击对象存储——&gt;<span style="color: black;">而后</span>点击新建空间</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/6bf9a040844144e38b26fc4e7c5f1371~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=NTzHO%2FbChyfAQAMlANzWnCjIObs%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">填写表单完成存储空间的创建,<span style="color: black;">完后后七牛云会自动为我分配一个测试域名,<span style="color: black;">这般</span><span style="color: black;">咱们</span>就<span style="color: black;">能够</span><span style="color: black;">运用</span>这个域名进行上传/下载文件了。</span></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">需要<span style="color: black;">重视</span>的是:测试域名只能<span style="color: black;">运用</span>30天!!并且测试域名只能<span style="color: black;">运用</span>HTTP协议,不支持HTTPS协议</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/2197ab7d6b0a45a7bf8ba00d947670a3~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=PYfu%2BCodnXG10cJgjax9aN8bPL8%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 对象存储服务绑定域名</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">由于</span>我<span style="color: black;">已然</span>购买过域名cuiliangblog.cn。<span style="color: black;">因此</span>接下来绑定oss.cuiliangblog.cn给这个存储空间<span style="color: black;">就可</span>。需要<span style="color: black;">重视</span>的是,虽然七牛云的对象存储服务免费,<span style="color: black;">然则</span>CDN加速服务是收费的,我<span style="color: black;">已然</span>够买过腾讯CDN服务,<span style="color: black;">因此</span>此处配置了自定义源站域名,<span style="color: black;">大众</span><span style="color: black;">能够</span><span style="color: black;">根据</span>自己的<span style="color: black;">实质</span><span style="color: black;">状况</span><span style="color: black;">选取</span>最合适的配置。</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/0166f9549fa34d79a69bf182a20fbb11~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=U%2BGa44Peg3VmhynX3EuU55vtPps%3D" style="width: 50%; margin-bottom: 20px;"></div>需要<span style="color: black;">重视</span>的是<span style="color: black;">倘若</span><span style="color: black;">运用</span>第三方CDN服务,记住这个默认分配的CNAME,CDN配置回源策略时,填写这个CNAME。<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/ea679f6c650a4b3fac2418876e5dbe6b~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=xMSHPaYFEFw4MYHmgeglUdgXzjg%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">3、</span>CDN配置</h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 添加CDN加速域名</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">登录腾讯云——&gt;<span style="color: black;">掌控</span>台——&gt;CDN内容分发网络——&gt;域名管理——&gt;添加域名</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/8eff198c009143698ee79b81cfedadbe~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=%2FVxOy%2B%2F4BxT5if2pGsWVpHZXIbQ%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 配置回源及缓存等策略</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">此处以我的oss.cuiliangblog.cn对象存储域名举例,其中回源策略填写七牛云对象存储的CNAME。</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/7ffe719645fa413d97f53898c2fcd50d~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=WslXacVKlYN%2BndQdI8wUEa1GtZc%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">4、</span>DNS配置</h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 添加域名解析记录</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">登录阿里云——&gt;<span style="color: black;">掌控</span>台——&gt;域名——&gt;解析</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/4deaf166a97949e2bce143bff92bfe7a~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=kU9fUIs7kd9YeqsYwLAORFLYvxo%3D" style="width: 50%; margin-bottom: 20px;"></div>新增一条oss.cuiliangblog.cn的CNAME域名解析记录,记录值填写腾讯云CDN的CNAME值<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/ce915234486c4791b336f574f4bd8d2e~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=kP3AfaEhVqXXzN5xwGvZyQ2BMi8%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. <span style="color: black;">拜访</span>验证</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">至此,存储服务和CDN以及DNS配置已<span style="color: black;">所有</span>完成,接下来做一个简单的测试</p>七牛云——&gt;<span style="color: black;">掌控</span>台——&gt;对象存储——&gt;空间管理——&gt;文件管理——&gt;上传文件,随便<span style="color: black;">选取</span>一张<span style="color: black;">照片</span>资源上传<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/7f914fe71b3c42aeb6eaff2788ae3583~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=7Hpi5CkMBOasEq0478MknCFO8gE%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/60b8c1d8575d4e57bd76418feae68610~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=OsQ7sn55BCYYPIWmzdD%2FP6ITLjo%3D" style="width: 50%; margin-bottom: 20px;"></div>随便上传一张<span style="color: black;">照片</span>后,点击返回,查看<span style="color: black;">照片</span>资源外链<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/37c06d953463429c9a9bfe9f3df07a1e~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=8BiUod7zwjv6mbWqgRcL4%2BH%2B3Nc%3D" style="width: 50%; margin-bottom: 20px;"></div><span style="color: black;">运用</span>浏览器<span style="color: black;">拜访</span>验证域名解析<span style="color: black;">是不是</span>正常<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/5664cb83a50a4939be4cbe24ef842b89~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=eya3WEJ1BGNnWb%2BsE3mfb7zRAKE%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/54ada34cb70e4451a15d409719155ec6~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=kaA7N%2FGx45WOuJRtxaMCRz39cLU%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">经过<span style="color: black;">拜访</span>测试,上传<span style="color: black;">照片</span>后生成的外链<span style="color: black;">能够</span>正常打开<span style="color: black;">拜访</span>,且远程<span style="color: black;">位置</span>为腾讯云CDN加速节点。到这儿,运维的工作<span style="color: black;">已然</span>完<span style="color: black;">成为了</span>,接下来角色转换,<span style="color: black;">此刻</span>是一位专业的后端<span style="color: black;">研发</span>工程师。</p>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">5、</span>后端-token接口<span style="color: black;">研发</span></h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 后端功能模块概述</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;"><span style="color: black;">想要</span><span style="color: black;">运用</span>七牛的对象存储服务上传文件,就需要在后端<span style="color: black;">经过</span>七牛SDK生成的一个安全凭证,<span style="color: black;">仅有</span>客户端拿着这个上传凭证上传文件才是有效的,否则七牛服务器是不接受的。</span>七牛云的<span style="color: black;"><span style="color: black;">研发</span>者中心</span><span style="color: black;">供给</span>了<span style="color: black;">非常多</span>版本的SDK,例如Go,JavaScript,PHP,Python,Node.js,Ruby,C#,C/C++等等,<span style="color: black;">这儿</span>是我<span style="color: black;">运用</span>的是python的SDK,详见<span style="color: black;">研发</span>者文档:<span style="color: black;">https://developer.qiniu.com/kodo/1242/python</span></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 获取accessKey和secretKey</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><span style="color: black;">经过</span>查看<span style="color: black;">研发</span>者文档可知,调用SDK需要传入<span style="color: black;">bucket、</span>accessKey、secretKey三个参数</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">bucket的值<span style="color: black;">便是</span>存储空间的名<span style="color: black;">叫作</span>,accessKey和secretKey<span style="color: black;">能够</span>将鼠标悬浮在右上角的头像上<span style="color: black;">而后</span>点击密钥管理,<span style="color: black;">而后</span>创新密钥</p>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/2a011d417ee04b55bd5ff807e62f2ec3~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=zlmGXMgkjh%2FDSxAIGoML%2BsvPI%2FI%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/6b113963e19f41bcb0eac7f1e4885d9d~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=HQRhj9P%2BhEUYdG2is3XZCSm48sA%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">3. DRF项目<span style="color: black;">关联</span>功能代码实现</strong></p>安装<span style="color: black;">SDK</span><span style="color: black;">pip</span> install qiniusetting.py中存放密钥信息<span style="color: black;"># 七牛OSS存储配置</span>

    <span style="color: black;">QINIU_AK</span> = <span style="color: black;">XXXXXXXXXXXXX</span>

    <span style="color: black;">QINIU_SK</span> = <span style="color: black;">XXXXXXXXXXXXX</span>

    <span style="color: black;">QINIU_BUCKET</span> = <span style="color: black;">cuiliangoss</span>

    <span style="color: black;">QINIU_DOMAIN</span> = <span style="color: black;">https://oss.cuiliangblog.cn/</span>配置请求token的API接口路由(urls.py)<span style="color: black;">from</span> django.urls <span style="color: black;">import</span> path
    <span style="color: black;">from</span> rest_framework <span style="color: black;">import</span> routers
    <span style="color: black;">from</span> public <span style="color: black;">import</span> views

    app_name = <span style="color: black;">"public"</span>
    urlpatterns = [
    path(<span style="color: black;">qiniuToken/</span>, views.QiniuTokenAPIView.as_view()),
    <span style="color: black;"># 获取七牛上传token</span>…………
    ]
    router = routers.DefaultRouter()
    urlpatterns += router.urls编写视图函数(views.py),<span style="color: black;">由于</span>此处仅处理简单的响应,不<span style="color: black;">触及</span>到模型操作,直接<span style="color: black;">运用</span>一级视图<span style="color: black;">就可</span><span style="color: black;">from</span> rest_framework <span style="color: black;">import</span> status
    <span style="color: black;">from</span>rest_framework.response<span style="color: black;">import</span> Response
    <span style="color: black;">from</span> rest_framework.views <span style="color: black;">import</span> APIView
    <span style="color: black;">from</span> qiniu <span style="color: black;">import</span> Auth
    <span style="color: black;">from</span> django.conf <span style="color: black;">import</span> settings


    <span style="color: black;"><span style="color: black;">class</span> <span style="color: black;">QiniuTokenAPIView</span><span style="color: black;">(APIView)</span>:</span>
    <span style="color: black;">"""
      获取七牛上传文件token
      """</span>

    <span style="color: black;"><span style="color: black;">def</span> <span style="color: black;">get</span><span style="color: black;">(request)</span>:</span>q = Auth(settings.QINIU_AK, settings.QINIU_SK)
    token = q.upload_token(settings.QINIU_BUCKET)<span style="color: black;">return</span> Response({<span style="color: black;">token</span>: token, <span style="color: black;">domain</span>: settings.QINIU_DOMAIN}, status=status.HTTP_200_OK)<span style="color: black;">运用</span>API接口工具<span style="color: black;">拜访</span>测试<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/cf7ce71ccfea4fb3a14b8fbc0b59349c~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=AkXtWHpgrpZ7C2XyqRzDC1yHVBM%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">至此,后端API接口<span style="color: black;">研发</span>完成,短短几行代码,<span style="color: black;">容易</span>而愉快的完<span style="color: black;">成为了</span>后端的<span style="color: black;">研发</span>。接来下才是<span style="color: black;">全部</span>项目最核心的部分,苦逼的前端工程师上岗了。</p>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">6、</span>前端-上传组件<span style="color: black;">研发</span></h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 上传组件分析</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">七牛对象存储支持多种多样的类型文件上传,虽然官方<span style="color: black;">供给</span>了<span style="color: black;">仔细</span>的demo示例,<span style="color: black;">然则</span>在<span style="color: black;">实质</span><span style="color: black;">研发</span><span style="color: black;">运用</span>过程中,为了便于多个<span style="color: black;">区别</span>项目的移植和以及vue组件调用,<span style="color: black;">因此呢</span>将其封装为js的模块,当需要调用<span style="color: black;">运用</span>七牛的对象存储服务上传文件时,只需要传入上传文件的路径和文件对象<span style="color: black;">就可</span>。函数在执行时,先请求后端API接口,获取<span style="color: black;">这次</span>上传文件的token和domain,并提取文件名加入时间戳,避免同一时间传入多张<span style="color: black;">照片</span><span style="color: black;">引起</span>文件名冲突,最后调用七牛<span style="color: black;">JavaScript-SDK实现文件上传,并返回成功上传的文件URL<span style="color: black;">位置</span>。<span style="color: black;">仔细</span>说明请参考官方文档:</span><span style="color: black;">https://developer.qiniu.com/kodo/1283/javascript</span></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 上传组件代码实现</strong></p>API请求封装,<span style="color: black;">详细</span>请参考以前发布的<span style="color: black;">文案</span><span style="color: black;">https://www.cuiliangblog.cn/detail/article/12</span><span style="color: black;">import</span> index <span style="color: black;">from</span> <span style="color: black;">./index</span>

    <span style="color: black;">// 获取七牛<span style="color: black;">照片</span>上传token</span>
    <span style="color: black;">export</span> <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">getQiNiuToken</span>() </span>{
    <span style="color: black;">return</span> index.get(<span style="color: black;">public/qiniuToken/</span>)
    }七牛文件上传模块<span style="color: black;">import</span> * <span style="color: black;">as</span> qiniu <span style="color: black;">from</span> <span style="color: black;">"qiniu-js"</span>;
    <span style="color: black;">import</span> {getQiNiuToken} <span style="color: black;">from</span> <span style="color: black;">"@/api/public"</span>;

    <span style="color: black;"><span style="color: black;">function</span> <span style="color: black;">qiniuUpload</span>() </span>{ <span style="color: black;">//file是<span style="color: black;">选取</span>的文件对象</span>
    <span style="color: black;">const</span> upload = <span style="color: black;">(<span style="color: black;">dir, file</span>) =&gt;</span> {
    <span style="color: black;">return</span> <span style="color: black;">new</span> <span style="color: black;">Promise</span>(<span style="color: black;">(<span style="color: black;">resolve, reject</span>) =&gt;</span> {
    getQiNiuToken().then(<span style="color: black;">(<span style="color: black;">response</span>) =&gt;</span> {
    <span style="color: black;">let</span>domain = response.domain<span style="color: black;">let</span> token = response.token
    <span style="color: black;">let</span> key = dir + <span style="color: black;">/</span> + file.name.substring(<span style="color: black;">0</span>, file.name.lastIndexOf(<span style="color: black;">.</span>)) + <span style="color: black;">-</span> + <span style="color: black;">new</span> <span style="color: black;">Date</span>().getTime()
    + file.name.substring(file.name.lastIndexOf(<span style="color: black;">.</span>))
    <span style="color: black;">let</span> config = {
    <span style="color: black;">useCdnDomain</span>: <span style="color: black;">true</span>, <span style="color: black;">//<span style="color: black;">暗示</span><span style="color: black;">是不是</span><span style="color: black;">运用</span> cdn 加速域名,为布尔值,true <span style="color: black;">暗示</span><span style="color: black;">运用</span>,默认为 false。</span>
    <span style="color: black;">region</span>: qiniu.region.z1 <span style="color: black;">// <span style="color: black;">按照</span><span style="color: black;">详细</span>提示修改上传地区,当为 null 或 undefined 时,自动分析上传域名区域</span>
    }
    <span style="color: black;">let</span> putExtra = {
    <span style="color: black;">fname</span>: <span style="color: black;">""</span>, <span style="color: black;">//文件原文件名</span>
    <span style="color: black;">params</span>: {}, <span style="color: black;">//用来<span style="color: black;">安置</span>自定义变量</span>
    <span style="color: black;">mimeType</span>: <span style="color: black;">null</span> <span style="color: black;">//用来限制上传文件类型,为 null 时<span style="color: black;">暗示</span>不对文件类型限制;限制类型放到数组里: ["image/png", "image/jpeg", "image/gif"]</span>
    };
    <span style="color: black;">const</span> observable = qiniu.upload(file, key, token, putExtra, config)
    observable.subscribe({
    <span style="color: black;">next</span>: <span style="color: black;">(<span style="color: black;">result</span>) =&gt;</span> {
    <span style="color: black;">//<span style="color: black;">重点</span>用来展示进度</span>
    <span style="color: black;">console</span>.log(result)
    },
    <span style="color: black;">error</span>: <span style="color: black;">(<span style="color: black;">error</span>) =&gt;</span> {
    <span style="color: black;">//上传错误后触发</span>
    <span style="color: black;">console</span>.log(error);
    reject(error)
    },
    <span style="color: black;">complete</span>: <span style="color: black;">(<span style="color: black;">result</span>) =&gt;</span> {
    <span style="color: black;">//上传成功后触发。<span style="color: black;">包括</span>文件<span style="color: black;">位置</span>。</span>
    <span style="color: black;">let</span>url = domain + result.key<span style="color: black;">// console.log(url)</span>
    resolve(url)
    },
    });
    }).catch(<span style="color: black;"><span style="color: black;">response</span> =&gt;</span> {
    <span style="color: black;">//<span style="color: black;">出现</span>错误时执行的代码</span>
    <span style="color: black;">console</span>.log(response)
    });
    })
    }
    <span style="color: black;">return</span>{
    upload
    }
    }<span style="color: black;">export</span> <span style="color: black;">default</span> qiniuUpload<h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">7、</span>前端-裁剪组件<span style="color: black;">研发</span></h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. 裁剪模块分析</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">用户上传<span style="color: black;">照片</span>并镜像预览裁剪操作在多个页面中都会<span style="color: black;">运用</span>到,<span style="color: black;">因此呢</span>非常有必要将它封装成一个公共的子组件。其他页面<span style="color: black;">运用</span>这个组件时,<span style="color: black;">照片</span>上传<span style="color: black;">位置</span>、<span style="color: black;">照片</span>宽度、<span style="color: black;">照片</span>高度都不尽相同。<span style="color: black;">因此呢</span>将这个三个值设为子组件的参数变量,当用户完成<span style="color: black;">照片</span>裁剪后,点击上传时,调用上传组件,并给父组件传递success事件,并<span style="color: black;">包括</span><span style="color: black;">最后</span><span style="color: black;">照片</span>的URL<span style="color: black;">位置</span>参数。</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. 裁剪组件代码实现</strong></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">裁剪组件基于element-plus(参考<span style="color: black;">位置</span>:<span style="color: black;">https://github.com/element-plus/element-plus</span>)和vue-cropper(参考<span style="color: black;">位置</span>:<span style="color: black;">https://github.com/xyxiao001/vue-cropper</span>)二次封装实现</p>用户<span style="color: black;">照片</span>裁剪组件(UploadImg.vue)<span style="color: black;">&lt;<span style="color: black;">template</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-upload</span> <span style="color: black;">accept</span>=<span style="color: black;">".jpg,.jpeg,.png"</span>
      <span style="color: black;">action</span>=<span style="color: black;">"./"</span>
      <span style="color: black;">:auto-upload</span>=<span style="color: black;">"false"</span>
      <span style="color: black;">:on-change</span>=<span style="color: black;">"uploadChange"</span>
      <span style="color: black;">:show-file-list</span>=<span style="color: black;">"false"</span>
      &gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">class</span>=<span style="color: black;">"upload-btn"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">class</span>=<span style="color: black;">"upload-icon"</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-upload-img"</span>/&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">p</span>&gt;</span><span style="color: black;">选取</span><span style="color: black;">照片</span><span style="color: black;">&lt;/<span style="color: black;">p</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-upload</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-dialog</span> <span style="color: black;">title</span>=<span style="color: black;">"<span style="color: black;">照片</span>裁剪"</span> <span style="color: black;">v-model</span>=<span style="color: black;">"showCopper"</span> <span style="color: black;">append-to-body</span> <span style="color: black;">center</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span> <span style="color: black;">class</span>=<span style="color: black;">"cropper"</span> <span style="color: black;">v-loading</span>=<span style="color: black;">"loading"</span> <span style="color: black;">element-loading-text</span>=<span style="color: black;">"<span style="color: black;">照片</span>上传中..."</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">span</span> <span style="color: black;">class</span>=<span style="color: black;">"cropper-area"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">vueCropper</span>
      <span style="color: black;">ref</span>=<span style="color: black;">"cropper"</span>
      <span style="color: black;">:img</span>=<span style="color: black;">"cropImg"</span>
      <span style="color: black;">:autoCrop</span>=<span style="color: black;">"true"</span>
      <span style="color: black;">:autoCropWidth</span>=<span style="color: black;">"props.width"</span>
      <span style="color: black;">:autoCropHeight</span>=<span style="color: black;">"props.height"</span>
      <span style="color: black;">:fixedNumber</span>=<span style="color: black;">""</span>
      <span style="color: black;">:fixed</span>=<span style="color: black;">"true"</span>
      @<span style="color: black;">realTime</span>=<span style="color: black;">"realTime"</span>
      &gt;</span><span style="color: black;">&lt;/<span style="color: black;">vueCropper</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">span</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">span</span> <span style="color: black;">class</span>=<span style="color: black;">"preview-area"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">p</span>&gt;</span><span style="color: black;">照片</span>预览<span style="color: black;">&lt;/<span style="color: black;">p</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span> <span style="color: black;">class</span>=<span style="color: black;">"show-preview"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span> <span style="color: black;">:style</span>=<span style="color: black;">"previews.div"</span> <span style="color: black;">class</span>=<span style="color: black;">"preview"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">img</span> <span style="color: black;">:src</span>=<span style="color: black;">"previews.url"</span> <span style="color: black;">:style</span>=<span style="color: black;">"previews.img"</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">span</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">template</span> #<span style="color: black;">footer</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"success"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">label</span> <span style="color: black;">class</span>=<span style="color: black;">"pointer"</span> <span style="color: black;">for</span>=<span style="color: black;">"uploads"</span>&gt;</span>更换<span style="color: black;">照片</span><span style="color: black;">&lt;/<span style="color: black;">label</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">input</span> <span style="color: black;">type</span>=<span style="color: black;">"file"</span> <span style="color: black;">id</span>=<span style="color: black;">"uploads"</span> <span style="color: black;">style</span>=<span style="color: black;">"position:absolute; clip:rect(0 0 0 0);"</span>
      <span style="color: black;">accept</span>=<span style="color: black;">"image/png, image/jpeg, image/jpg"</span> @<span style="color: black;">change</span>=<span style="color: black;">"uploadChange($event)"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button-group</span> <span style="color: black;">class</span>=<span style="color: black;">"cropper-btn-group"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> <span style="color: black;">plain</span> @<span style="color: black;">click</span>=<span style="color: black;">"changeScale(1)"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-amplification"</span>/&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> <span style="color: black;">plain</span> @<span style="color: black;">click</span>=<span style="color: black;">"changeScale(-1)"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-narrow"</span>/&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> <span style="color: black;">plain</span> @<span style="color: black;">click</span>=<span style="color: black;">"changeReset()"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-reset"</span>/&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> <span style="color: black;">plain</span> @<span style="color: black;">click</span>=<span style="color: black;">"changeRotate(1)"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-clockwise-sense"</span>/&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> <span style="color: black;">plain</span> @<span style="color: black;">click</span>=<span style="color: black;">"changeRotate(-1)"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-clockwise-dirction"</span>/&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-button-group</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span> @<span style="color: black;">click</span>=<span style="color: black;">"showCopper=false"</span>&gt;</span>取 消<span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> @<span style="color: black;">click</span>=<span style="color: black;">"confirmFn"</span> <span style="color: black;">size</span>=<span style="color: black;">"medium"</span>&gt;</span>确 定<span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">template</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-dialog</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">template</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">script</span> <span style="color: black;">setup</span>&gt;</span><span style="color: black;">
      <span style="color: black;">import</span>{reactive, ref}<span style="color: black;">from</span> <span style="color: black;">vue</span>
      <span style="color: black;">import</span> icon <span style="color: black;">from</span> <span style="color: black;">"@/utils/icon"</span>;
      <span style="color: black;">import</span> timeFormat <span style="color: black;">from</span> <span style="color: black;">"@/utils/timeFormat"</span>;
      <span style="color: black;">import</span> <span style="color: black;">vue-cropper/dist/index.css</span>
      <span style="color: black;">import</span> {VueCropper} <span style="color: black;">from</span> <span style="color: black;">"vue-cropper"</span>;
      <span style="color: black;">import</span> qiniuUpload <span style="color: black;">from</span> <span style="color: black;">"@/utils/qiniuUpload"</span>;
      <span style="color: black;">import</span> {ElMessage} <span style="color: black;">from</span> <span style="color: black;">element-plus</span>

      <span style="color: black;">let</span> {MyIcon} = icon()
      <span style="color: black;">// 格式化处理时间</span>
      <span style="color: black;">let</span> {timeFile} = timeFormat()
      <span style="color: black;">// 七牛<span style="color: black;">照片</span>上传</span>
      <span style="color: black;">let</span> {upload} = qiniuUpload()
      <span style="color: black;">const</span> props = defineProps({
      <span style="color: black;">// <span style="color: black;">照片</span>宽度</span>
      <span style="color: black;">width</span>: {
      <span style="color: black;">type</span>: <span style="color: black;">Number</span>,
      <span style="color: black;">required</span>: <span style="color: black;">false</span>,
      <span style="color: black;">default</span>: <span style="color: black;">200</span>
      },
      <span style="color: black;">// <span style="color: black;">照片</span>高度</span>
      <span style="color: black;">height</span>: {
      <span style="color: black;">type</span>: <span style="color: black;">Number</span>,
      <span style="color: black;">required</span>: <span style="color: black;">false</span>,<span style="color: black;">default</span>: <span style="color: black;">200</span>
      },
      <span style="color: black;">// <span style="color: black;">照片</span><span style="color: black;">保留</span>目录</span>
      <span style="color: black;">dir</span>: {
      <span style="color: black;">type</span>: <span style="color: black;">String</span>,
      <span style="color: black;">required</span>: <span style="color: black;">true</span>,
      <span style="color: black;">default</span>: <span style="color: black;">upload</span>
      }
      })
      <span style="color: black;">// 定义事件(子组件向父组件传参)</span>
      <span style="color: black;">const</span> emit = defineEmits([<span style="color: black;">saveImg</span>]);
      <span style="color: black;">// 图像裁剪组件对象</span>
      <span style="color: black;">const</span>cropper = ref(<span style="color: black;">null</span>);
      <span style="color: black;">// 裁剪后的<span style="color: black;">照片</span>文件</span>
      <span style="color: black;">const</span> cropImg = ref();
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪对话框<span style="color: black;">是不是</span><span style="color: black;">表示</span></span>
      <span style="color: black;">const</span> showCopper = ref(<span style="color: black;">false</span>);
      <span style="color: black;">// 文件上传组件<span style="color: black;">选择</span><span style="color: black;">照片</span>事件</span>
      <span style="color: black;">const</span> uploadChange = <span style="color: black;">(<span style="color: black;">file</span>) =&gt;</span> {
      <span style="color: black;">let</span> fileObj
      <span style="color: black;">if</span> (<span style="color: black;">raw</span> <span style="color: black;">in</span> file) {
      <span style="color: black;">console</span>.log(<span style="color: black;">"element对象"</span>)
      fileObj = file.raw
      } <span style="color: black;">else</span> {
      <span style="color: black;">console</span>.log(<span style="color: black;">"原生对象"</span>)
      fileObj = file.target.files[<span style="color: black;">0</span>]
      }
      <span style="color: black;">const</span> reader = <span style="color: black;">new</span> FileReader();
      reader.onload = <span style="color: black;">(<span style="color: black;">event</span>) =&gt;</span>{
      cropImg.value = event.target.result;
      };
      reader.readAsDataURL(fileObj)
      showCopper.value =<span style="color: black;">true</span>;
      }
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪预览数据</span>
      <span style="color: black;">const</span> previews = reactive({})
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪预览事件</span>
      <span style="color: black;">const</span> realTime = <span style="color: black;">(<span style="color: black;">data</span>) =&gt;</span> {
      <span style="color: black;">Object</span>.assign(previews, data)
      }<span style="color: black;">// <span style="color: black;">照片</span>裁剪缩放事件</span>
      <span style="color: black;">const</span> changeScale = <span style="color: black;">(<span style="color: black;">num</span>) =&gt;</span> {
      num = num || <span style="color: black;">1</span>
      cropper.value.changeScale(num)
      }
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪旋转事件</span>
      <span style="color: black;">const</span> changeRotate = <span style="color: black;">(<span style="color: black;">num</span>) =&gt;</span>{<span style="color: black;">if</span> (num === <span style="color: black;">1</span>) {
      cropper.value.rotateLeft()
      } <span style="color: black;">else</span> {
      cropper.value.rotateRight()
      }
      }
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪重置事件</span>
      <span style="color: black;">const</span> changeReset = <span style="color: black;"><span style="color: black;">()</span> =&gt;</span> {
      cropper.value.refresh()
      }
      <span style="color: black;">// 文件上传动画状态</span>
      <span style="color: black;">const</span> loading = ref(<span style="color: black;">false</span>)
      <span style="color: black;">// <span style="color: black;">照片</span>裁剪完成上传事件</span>
      <span style="color: black;">const</span> confirmFn = <span style="color: black;"><span style="color: black;">()</span> =&gt;</span> {
      <span style="color: black;">// 获取blob对象</span>cropper.value.getCropBlob(<span style="color: black;"><span style="color: black;">blobData</span> =&gt;</span> {
      <span style="color: black;">console</span>.log(blobData)
      loading.value = <span style="color: black;">true</span>
      <span style="color: black;">//blob转file</span>
      <span style="color: black;">const</span> file = <span style="color: black;">new</span> File(, timeFile(<span style="color: black;">Date</span>.now()) + <span style="color: black;">.jpg</span>, {<span style="color: black;">type</span>: blobData.type});<span style="color: black;">console</span>.log(file)
      upload(props.dir, file).then(<span style="color: black;">(<span style="color: black;">response</span>) =&gt;</span> {
      <span style="color: black;">console</span>.log(response)
      ElMessage({
      <span style="color: black;">message</span>: <span style="color: black;"><span style="color: black;">照片</span>上传成功!</span>,
      <span style="color: black;">type</span>: <span style="color: black;">success</span>,
      })
      emit(<span style="color: black;">saveImg</span>, response)
      showCopper.value = <span style="color: black;">false</span>
      loading.value = <span style="color: black;">false</span>
      }).catch(<span style="color: black;"><span style="color: black;">response</span> =&gt;</span> {
      <span style="color: black;">//<span style="color: black;">出现</span>错误时执行的代码</span>
      <span style="color: black;">console</span>.log(response)
      ElMessage.error(<span style="color: black;"><span style="color: black;">照片</span>上传失败!</span>)
      loading.value = <span style="color: black;">false</span>
      });
      })
      }
    </span><span style="color: black;">&lt;/<span style="color: black;">script</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">style</span> <span style="color: black;">scoped</span> <span style="color: black;">lang</span>=<span style="color: black;">"scss"</span>&gt;</span>.upload-btn {
    .upload-icon {
    font-size: 24px;
    color: $color-text-secondary;
    vertical-align: -7 px !important;
    margin-right: 5px;
    }

    p {
    display: inline-block;
    vertical-align: 4px;
    }
    }

    .cropper {
    display: flex;
    height: 50vh;

    .cropper-area {
    flex: 2;
    }

    .preview-area {
    flex: 1;
    margin-left: 20px;

    p {
    text-align: center;
    margin-bottom: 20px;
    }

    .show-preview {
    flex: 1;
    -webkit-flex: 1;
    display: flex;
    display: -webkit-flex;
    justify-content: center;
    -webkit-justify-content: center;

    .preview {
    overflow: hidden;
    border-radius: 50%;
    border: 1px solid #cccccc;
    bac<span style="color: black;">公斤</span>round: #cccccc;
    }
    }
    }
    }

    .cropper-btn-group {
    margin: 0 40px;

    .anticon {
    font-size: 18px;
    }
    }<span style="color: black;">&lt;/<span style="color: black;">style</span>&gt;</span>其他vue页面调用<span style="color: black;">照片</span>上传组件时,传入裁剪完成后<span style="color: black;">照片</span>的宽度,高度,以及文件上传目录。当完成上传操作后,<span style="color: black;">照片</span>裁剪组件会返回一个上传完成事件,并携带<span style="color: black;">照片</span>URL<span style="color: black;">位置</span>。<span style="color: black;">&lt;<span style="color: black;">template</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span> <span style="color: black;">class</span>=<span style="color: black;">"page"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span> <span style="color: black;">class</span>=<span style="color: black;">"animate__animated animate__zoomIn"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-card</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">template</span> #<span style="color: black;">header</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">span</span> <span style="color: black;">class</span>=<span style="color: black;">"card-title no-choose"</span>&gt;</span><span style="color: black;">&lt;<span style="color: black;">MyIcon</span> <span style="color: black;">type</span>=<span style="color: black;">"icon-form-color"</span>/&gt;</span> 申请表单<span style="color: black;">&lt;/<span style="color: black;">span</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">template</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form</span> <span style="color: black;">ref</span>=<span style="color: black;">"linkFormRef"</span> <span style="color: black;">:model</span>=<span style="color: black;">"linkForm"</span> <span style="color: black;">label-width</span>=<span style="color: black;">"120px"</span> <span style="color: black;">:rules</span>=<span style="color: black;">"rules"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form-item</span> <span style="color: black;">label</span>=<span style="color: black;">"网站名<span style="color: black;">叫作</span>"</span> <span style="color: black;">prop</span>=<span style="color: black;">"name"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-input</span> <span style="color: black;">v-model</span>=<span style="color: black;">"linkForm.name"</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">el-input</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form-item</span> <span style="color: black;">label</span>=<span style="color: black;">"网站<span style="color: black;">位置</span>"</span> <span style="color: black;">prop</span>=<span style="color: black;">"url"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-input</span> <span style="color: black;">v-model</span>=<span style="color: black;">"linkForm.url"</span> <span style="color: black;">placeholder</span>=<span style="color: black;">"请输入完整<span style="color: black;">位置</span>,https://开头"</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">el-input</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form-item</span> <span style="color: black;">label</span>=<span style="color: black;">"网站简介"</span> <span style="color: black;">prop</span>=<span style="color: black;">"describe"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-input</span> <span style="color: black;">v-model</span>=<span style="color: black;">"linkForm.describe"</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">el-input</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form-item</span> <span style="color: black;">label</span>=<span style="color: black;">"网站logo"</span> <span style="color: black;">prop</span>=<span style="color: black;">"logo"</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">span</span> <span style="color: black;">v-if</span>=<span style="color: black;">"linkForm.logo==="</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">UploadImg</span> <span style="color: black;">:width</span>=<span style="color: black;">"150"</span> <span style="color: black;">:height</span>=<span style="color: black;">"150"</span> <span style="color: black;">:dir</span>=<span style="color: black;">"logo"</span> @<span style="color: black;">saveImg</span>=<span style="color: black;">"saveImg"</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">UploadImg</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">span</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">span</span> <span style="color: black;">v-else</span>&gt;</span><span style="color: black;">&lt;<span style="color: black;">el-avatar</span> <span style="color: black;">:size</span>=<span style="color: black;">"100"</span> <span style="color: black;">:src</span>=<span style="color: black;">"linkForm.logo"</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">el-avatar</span>&gt;</span><span style="color: black;">&lt;/<span style="color: black;">span</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> <span style="color: black;">type</span>=<span style="color: black;">"primary"</span> @<span style="color: black;">click</span>=<span style="color: black;">"onSubmit"</span>&gt;</span>提交<span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;<span style="color: black;">el-button</span> @<span style="color: black;">click</span>=<span style="color: black;">"reset"</span>&gt;</span>重置<span style="color: black;">&lt;/<span style="color: black;">el-button</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form-item</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-form</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">el-card</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">div</span>&gt;</span>
    <span style="color: black;">&lt;/<span style="color: black;">template</span>&gt;</span>

    <span style="color: black;">&lt;<span style="color: black;">script</span> <span style="color: black;">setup</span>&gt;</span><span style="color: black;">
      <span style="color: black;">import</span> UploadImg <span style="color: black;">from</span> <span style="color: black;">"@/components/common/UploadImg.vue"</span>
      <span style="color: black;">import</span> {onMounted, reactive, ref} <span style="color: black;">from</span> <span style="color: black;">"vue"</span>;
      <span style="color: black;">import</span> {getSiteConfig, postLink} <span style="color: black;">from</span> <span style="color: black;">"@/api/management"</span>;
      <span style="color: black;">import</span> icon <span style="color: black;">from</span> <span style="color: black;">"@/utils/icon"</span>;
      <span style="color: black;">import</span>{ElMessage}<span style="color: black;">from</span> <span style="color: black;">"element-plus"</span>;

      <span style="color: black;">let</span> {MyIcon} = icon()
      <span style="color: black;">// <span style="color: black;">照片</span>上传成功事件</span>
      <span style="color: black;">const</span> saveImg = <span style="color: black;">(<span style="color: black;">url</span>) =&gt;</span> {
      <span style="color: black;">console</span>.log(url)
      linkForm.logo = url
      }
      <span style="color: black;">// 提交友链表单对象</span>
      <span style="color: black;">const</span> linkFormRef = ref(<span style="color: black;">null</span>)
      <span style="color: black;">// 提交友链表单</span>
      <span style="color: black;">const</span> linkForm = reactive({
      <span style="color: black;">url</span>: ,
      <span style="color: black;">name</span>: ,
      <span style="color: black;">describe</span>: ,
      <span style="color: black;">logo</span>: ,
      })
      <span style="color: black;">// 表单验证规则</span>
      <span style="color: black;">const</span> rules = {
      <span style="color: black;">url</span>: [{<span style="color: black;">required</span>: <span style="color: black;">true</span>, <span style="color: black;">message</span>: <span style="color: black;">请输入网站<span style="color: black;">位置</span></span>, <span style="color: black;">trigger</span>: <span style="color: black;">blur</span>,}],
      <span style="color: black;">name</span>: [{<span style="color: black;">required</span>: <span style="color: black;">true</span>, <span style="color: black;">message</span>: <span style="color: black;">请输入网站名<span style="color: black;">叫作</span></span>, <span style="color: black;">trigger</span>: <span style="color: black;">blur</span>,}],
      <span style="color: black;">describe</span>: [{<span style="color: black;">required</span>: <span style="color: black;">true</span>, <span style="color: black;">message</span>: <span style="color: black;">请输入网站描述</span>, <span style="color: black;">trigger</span>: <span style="color: black;">blur</span>,}],
      <span style="color: black;">logo</span>: [{<span style="color: black;">required</span>: <span style="color: black;">true</span>, <span style="color: black;">message</span>: <span style="color: black;">请上传网站logo</span>, <span style="color: black;">trigger</span>: <span style="color: black;">blur</span>,}],
      }
      <span style="color: black;">// 提交表单事件</span>
      <span style="color: black;">const</span> onSubmit = <span style="color: black;"><span style="color: black;">()</span> =&gt;</span> {
      <span style="color: black;">console</span>.log(<span style="color: black;">submit!</span>)
      linkFormRef.value.validate(<span style="color: black;">(<span style="color: black;">valid</span>) =&gt;</span> {
      <span style="color: black;">if</span> (valid) {
      postLink(linkForm).then(<span style="color: black;">(<span style="color: black;">response</span>) =&gt;</span> {
      <span style="color: black;">console</span>.log(response)
      ElMessage({
      <span style="color: black;">message</span>: <span style="color: black;">友链申请提交成功,请耐心等待审核!</span>,
      <span style="color: black;">type</span>: <span style="color: black;">success</span>,
      })
      linkForm.url =
      linkForm.name =
      linkForm.describe =
      linkForm.logo =
      }).catch(<span style="color: black;"><span style="color: black;">response</span> =&gt;</span> {
      <span style="color: black;">//<span style="color: black;">出现</span>错误时执行的代码</span>
      <span style="color: black;">console</span>.log(response)<span style="color: black;">for</span> (<span style="color: black;">let</span> i <span style="color: black;">in</span> response) {
      ElMessage.error(response[<span style="color: black;">0</span>])
      }
      });
      }
      })
      }
      <span style="color: black;">// 重置表单</span>
      <span style="color: black;">const</span> reset = <span style="color: black;"><span style="color: black;">()</span>=&gt;</span> {
      linkFormRef.value.resetFields()
      }
      onMounted(<span style="color: black;"><span style="color: black;">()</span> =&gt;</span> {
      siteConfigData()
      })
    </span><span style="color: black;">&lt;/<span style="color: black;">script</span>&gt;</span>

    <span style="color: black;">&lt;<span style="color: black;">style</span> <span style="color: black;">scoped</span> <span style="color: black;">lang</span>=<span style="color: black;">"scss"</span>&gt;</span><span style="color: black;">
      <span style="color: black;">.demo</span>{<span style="color: black;">margin</span>: <span style="color: black;">15px</span> <span style="color: black;">0</span>
      }

      <span style="color: black;">.point-text</span> {
      <span style="color: black;">line-height</span>: <span style="color: black;">30px</span>;
      <span style="color: black;">color</span>: $color-text-primary;
      }
    </span><span style="color: black;">&lt;/<span style="color: black;">style</span>&gt;</span>
    <h1 style="color: black; text-align: left; margin-bottom: 10px;"><span style="color: black;">8、</span>功能验证与演示</h1>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">一切准备就绪,接下来演示<span style="color: black;">照片</span>上传效果,<span style="color: black;">亦</span>可查看在线<span style="color: black;">位置</span>查看效果<span style="color: black;">https://www.cuiliangblog.cn/applyLink</span></p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">1. <span style="color: black;">照片</span>上传前</strong></p>表单<span style="color: black;">表示</span>上传组件按钮<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/5d93f9926e3647f6890310cd9270202d~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=YjBXqKhGMx76zBO4MwtjmYTbA9M%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">2. <span style="color: black;">照片</span>上传中</strong></p><span style="color: black;">选取</span>添加本地<span style="color: black;">照片</span>并<span style="color: black;">调节</span>尺寸<div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/94100ff6ff584f88a386212982928a20~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=oVI%2BQkHapKkTNvNFfe6dduGrFrI%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;">3. <span style="color: black;">照片</span>上传后</strong></p>调用七牛上传组件SDK,完成<span style="color: black;">照片</span>上传,并返回<span style="color: black;">照片</span>URL<span style="color: black;">位置</span>
    <div style="color: black; text-align: left; margin-bottom: 10px;"><img src="https://p3-sign.toutiaoimg.com/tos-cn-i-qvj2lq49k0/a72a8aaada83426a9d57a09f936dc031~noop.image?_iz=58558&amp;from=article.pc_detail&amp;lk3s=953192f4&amp;x-expires=1729552594&amp;x-signature=CJLzKOFqdbk8ZFlLfw1EJ57ZlT4%3D" style="width: 50%; margin-bottom: 20px;"></div>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;">至此,<span style="color: black;">全部</span>用户<span style="color: black;">照片</span>上传流程<span style="color: black;">研发</span>完成!</p>
    <p style="font-size: 16px; color: black; line-height: 40px; text-align: left; margin-bottom: 15px;"><strong style="color: blue;"><span style="color: black;">更加多</span>运维<span style="color: black;">研发</span><span style="color: black;">关联</span><span style="color: black;">文案</span>,欢迎<span style="color: black;">拜访</span>崔亮的博客 https://www.cuiliangblog.cn</strong></p>




页: [1]
查看完整版本: 详解用户照片上传流程