前端笔记之文件上传组件

自己封装的一个贼拉丑文件上传组件


哈哈哈哈哈哈,真的好丑。。。。

1.实现思路

首先是一个文件上传的区域,本身就是大的label元素,绑定了一个隐藏的input type=file,然后就往后传文件,监听文件变化后就将这个label隐藏,切换出显示进度条的页面,等进度条完成后再切换预览页面。
代码很粗糙,仅限于写个demo

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .upload-area,
    .uploading,
    .after-upload {
      width: 250px;
      height: 250px;
      border-radius: 5px;
      background-color: rgb(240, 234, 234);
      box-shadow: 5px 5px 5px rgb(190, 190, 190);
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .uploading {
      flex-direction: column;
    }

    .before-upload {
      width: 100%;
      height: 100%;
    }

    .label-upload {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    }

    .upload-area.status-0>.uploading,
    .upload-area.status-0>.after-upload {
      display: none;
    }

    .upload-area.status-1>.before-upload,
    .upload-area.status-1>.after-upload {
      display: none;
    }

    .upload-area.status-2>.before-upload,
    .upload-area.status-2>.uploading {
      display: none;
    }

    .progress {
      margin: 0 10%;
      width: 80%;
      height: 20px;
      border-radius: 10px;
      background-color: rgba(250, 250, 250, 0.5);
      box-shadow: 1px 1px 1px rgba(148, 148, 148, 0.3);
    }

    .progress>.progress-bar {
      width: var(--bar-width);
      height: 20px;
      border-radius: 10px;
      background-color: rgb(70, 111, 201);
    }

    .progress>.progress-str {
      color: rgb(119, 119, 119);
    }

    .img-proview {
      width: 100%;
      height: 100%;
      background-repeat: no-repeat;
      background-size: contain;
    }
    #uploadFile{
      position: absolute;
      opacity: 0;
      width: 100%;
      height: 100%;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <div class="upload-area status-0">
    <div class="before-upload">
      <input type="file" id="uploadFile" onchange="uploadFile()" />
      <div class="label-upload">
        <svg t="1685411854880" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
          p-id="2357" width="80" height="80">
          <path
            d="M930.738818 418.535831H605.170853V93.238515A93.080637 93.080637 0 0 0 512.135324 0a93.058083 93.058083 0 0 0-93.012974 93.261069v325.500303H93.396506a93.306177 93.306177 0 0 0-93.328731 93.261069 93.441502 93.441502 0 0 0 27.222759 66.173634 92.967866 92.967866 0 0 0 65.857878 27.561071h325.973938V930.806367c0 25.756745 10.216993 49.077651 27.087435 65.902986a92.697218 92.697218 0 0 0 65.835323 27.312976 93.148299 93.148299 0 0 0 93.125745-93.215962V605.734592h325.567965c51.490936 0 93.238515-42.108444 93.215961-93.59938-0.022554-51.468382-41.770133-93.59938-93.215961-93.599381z"
            p-id="2358" fill="#cdcdcd"></path>
        </svg>
      </div>
    </div>
    <div class="uploading">
      <div class="progress-str"><span class="number">0</span>%</div>
      <div class="progress">
        <div class="progress-bar" style="--bar-width: 0%"></div>
      </div>
    </div>
    <div class="after-upload">
      <div class="img-proview">

      </div>
    </div>
  </div>
  <script>
    const uploadArea = document.querySelector(".upload-area");
    const progressBar = document.querySelector(".progress>.progress-bar");
    const progressNumber = document.querySelector(".uploading .number");
    function changeProgress(progress) {
      progressBar.setAttribute("style", `--bar-width:${progress}%`);
      progressNumber.innerText = `${progress}`;
    }
    async function uploadFile() {
      uploadArea.setAttribute("class", "upload-area status-1")
      const fileInput = document.querySelector("#uploadFile");
      if (fileInput.files.length === 0) {
        alert("请选择上传文件");
        return null;
      }
      upload(fileInput.files[0], oncomplete)
    }

    function upload(file, oncomplete) {
      const xhr = new XMLHttpRequest();
      xhr.onload = function () {
        const resp = JSON.parse(xhr.responseText);
        oncomplete(resp);
      }
      const formData = new FormData();
      formData.append('file', file);
      xhr.upload.onprogress = (e) => {
        console.log(e);
        // e下面有loaded属性为已经上传了多少数据,total为上传文件的总进度
        changeProgress(Math.floor(e.loaded / e.total * 100));
      };
      xhr.open("POST", "/local-api/file/singleFileUpload", true);
      xhr.send(formData);
    }

    function oncomplete(resp) {
      console.log(resp);
      uploadArea.setAttribute("class", "upload-area status-2")
      document.querySelector(".img-proview").setAttribute("style", `background-image: url("/local-api/${resp.data.src.replaceAll("\\", "/")}");`);
    }
  </script>
</body>

</html>

2拖拽上传

要想实现拖拽上传很简单只需要把上面的input取消隐藏,然后放大到撑满整个容器,设置opacity为0即可。但是,如果不想这么写,(是的,我不想),那么可以借助h5的ondrayenter、ondrayover、ondrop等api实现自己的一个可拖拽容器。
代码如下:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title>Title</title>
  <style>
    .upload-area,
    .uploading,
    .after-upload {
      width: 250px;
      height: 250px;
      border-radius: 5px;
      background-color: rgb(240, 234, 234);
      box-shadow: 5px 5px 5px rgb(190, 190, 190);
      display: flex;
      justify-content: center;
      align-items: center;
    }

    .uploading {
      flex-direction: column;
    }

    .before-upload {
      width: 100%;
      height: 100%;
    }

    .label-upload {
      width: 100%;
      height: 100%;
      display: flex;
      justify-content: center;
      align-items: center;
      cursor: pointer;
    }

    .upload-area.status-0>.uploading,
    .upload-area.status-0>.after-upload {
      display: none;
    }

    .upload-area.status-1>.before-upload,
    .upload-area.status-1>.after-upload {
      display: none;
    }

    .upload-area.status-2>.before-upload,
    .upload-area.status-2>.uploading {
      display: none;
    }

    .progress {
      margin: 0 10%;
      width: 80%;
      height: 20px;
      border-radius: 10px;
      background-color: rgba(250, 250, 250, 0.5);
      box-shadow: 1px 1px 1px rgba(148, 148, 148, 0.3);
    }

    .progress>.progress-bar {
      width: var(--bar-width);
      height: 20px;
      border-radius: 10px;
      background-color: rgb(70, 111, 201);
    }

    .progress>.progress-str {
      color: rgb(119, 119, 119);
    }

    .img-proview {
      width: 100%;
      height: 100%;
      background-repeat: no-repeat;
      background-size: contain;
      position: relative;
    }
    .img-proview>.close-button{
      width: 38px;
      height: 38px;
      border-radius: 50%;
      background-image: radial-gradient(rgb(173, 170, 170), transparent );
      filter: drop-shadow(1px 1px 1px rgba(148, 148, 148, 0.3));
      position: absolute;
      top: 0;
      right: 0;
      cursor: pointer;
    }
  </style>
</head>

<body>
  <div class="upload-area status-0">
    <div class="before-upload">
      <input type="file" id="uploadFile" style="display: none;" onchange="uploadFile()" />
      <label for="uploadFile" class="label-upload">
        <svg t="1685411854880" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
          p-id="2357" width="80" height="80">
          <path
            d="M930.738818 418.535831H605.170853V93.238515A93.080637 93.080637 0 0 0 512.135324 0a93.058083 93.058083 0 0 0-93.012974 93.261069v325.500303H93.396506a93.306177 93.306177 0 0 0-93.328731 93.261069 93.441502 93.441502 0 0 0 27.222759 66.173634 92.967866 92.967866 0 0 0 65.857878 27.561071h325.973938V930.806367c0 25.756745 10.216993 49.077651 27.087435 65.902986a92.697218 92.697218 0 0 0 65.835323 27.312976 93.148299 93.148299 0 0 0 93.125745-93.215962V605.734592h325.567965c51.490936 0 93.238515-42.108444 93.215961-93.59938-0.022554-51.468382-41.770133-93.59938-93.215961-93.599381z"
            p-id="2358" fill="#cdcdcd"></path>
        </svg>
      </label>
    </div>
    <div class="uploading">
      <div class="progress-str"><span class="number">0</span>%</div>
      <div class="progress">
        <div class="progress-bar" style="--bar-width: 0%"></div>
      </div>
    </div>
    <div class="after-upload">
      <div class="img-proview">
        <div class="close-button" onclick="closeCurrentImg()">
          <svg t="1685436626958" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1443" width="38" height="38"><path d="M512 85.333333C276.352 85.333333 85.333333 276.352 85.333333 512s191.018667 426.666667 426.666667 426.666667 426.666667-191.018667 426.666667-426.666667S747.648 85.333333 512 85.333333zM671.061333 662.848c-19.242667 19.221333-50.410667 19.221333-69.632 0l-81.216-81.216-81.216 81.216c-19.242667 19.221333-50.410667 19.221333-69.632 0-19.221333-19.242667-19.221333-50.410667 0-69.632L450.581333 512l-81.237333-81.216c-19.221333-19.242667-19.221333-50.410667 0-69.632 19.242667-19.221333 50.410667-19.221333 69.632 0l81.216 81.216 81.216-81.216c19.242667-19.221333 50.410667-19.242667 69.632 0 19.221333 19.242667 19.221333 50.410667 0 69.632L589.824 512l81.237333 81.216C690.282667 612.458667 690.304 643.626667 671.061333 662.848z" fill="#dbdbdb" p-id="1444"></path></svg>
        </div>
      </div>
    </div>
  </div>
  <script>
    const uploadArea = document.querySelector(".upload-area");
    const beforeUpload = document.querySelector(".label-upload");
    const progressBar = document.querySelector(".progress>.progress-bar");
    const progressNumber = document.querySelector(".uploading .number");
    // 注册div的拖拽api
    beforeUpload.ondragenter = e => {
      e.preventDefault();
    };
    beforeUpload.ondragover = e => {
      e.preventDefault();
    };
    beforeUpload.ondrop = e => {
      e.preventDefault();
      document.querySelector("#uploadFile").files = e.dataTransfer.files
      uploadFile();
    };
    

    function changeProgress(progress) {
      console.log("progress", progress)
      progressBar.setAttribute("style", `--bar-width:${progress}%`);
      progressNumber.innerText = `${progress}`;
    }
    async function uploadFile() {
      uploadArea.classList.replace("status-0", "status-1");
      const fileInput = document.querySelector("#uploadFile");
      if (fileInput.files.length === 0) {
        alert("请选择上传文件");
        return null;
      }
      upload(fileInput.files[0], oncomplete)
    }

    function upload(file, oncomplete) {
      const xhr = new XMLHttpRequest();
      xhr.onload = function () {
        const resp = JSON.parse(xhr.responseText);
        oncomplete(resp);
      }
      const formData = new FormData();
      formData.append('file', file);
      xhr.upload.onprogress = (e) => {
        console.log(e);
        // e下面有loaded属性为已经上传了多少数据,total为上传文件的总进度
        changeProgress(Math.floor(e.loaded / e.total * 100));
      };
      xhr.open("POST", "/local-api/file/singleFileUpload", true);
      xhr.send(formData);
    }

    function oncomplete(resp) {
      console.log(resp);
      uploadArea.classList.replace("status-1", "status-2");
      document.querySelector(".img-proview").setAttribute("style", `background-image: url("/local-api/${resp.data.src.replaceAll("\\", "/")}");`);
    }

    function closeCurrentImg(){
      uploadArea.classList.replace("status-2", "status-0");
      document.querySelector("#uploadFile").files = [];
    }
  </script>
</body>

</html>

3.后端代码

后端使用egg写的,话说tegg出来之后我是第一次用,而且看了文档之后还是懵逼,摸索了好几个小时才差不多搞懂了写法(我觉得还不是正规军)。唉
对应的后端代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值