乐于分享
好东西不私藏

源码顶级干货 | 本地图片直传服务器

源码顶级干货 | 本地图片直传服务器

先看效果

已关注

关注

重播 分享

单图上传

    后端部分(koa)

const Router = require('@koa/router')const router = new Router()const multer = require('@koa/multer')// 使用内存存储,文件不落地const upload = multer({ storage: multer.memoryStorage() })const { aliyun } = require('@/config/Account')const OSS = require('ali-oss')// 图片上传接口router.post('/upload', upload.single('file'), async ctx => {  try {    const file = ctx.file    // console.log(file, 'file');    if (!file) {      ctx.send([], 422'上传图片不能为空')      return    }    // 获取文件后缀    const ext = file.originalname.split('.').pop()    // 重新命名文件    const timestamp = Date.now()    const randomNum = Math.floor(Math.random() * 1000)    const uploadKey = `${aliyun.folder}${timestamp}${randomNum}.${ext}`    // 初始化 OSS    const client = new OSS({      region: aliyun.region,      accessKeyId: aliyun.accessKeyId,      accessKeySecret: aliyun.accessKeySecret,      bucket: aliyun.bucket,      securetrue    })    const headers = {      'Content-Disposition''inline',      'Content-Type': file.mimetype || 'image/jpg'    }    // 上传 Buffer    const result = await client.put(uploadKey, file.buffer, { headers })    // 成功    if (result.res.statusCode === 200) {      ctx.send('SUCCESS'200, result.url    } else {      throw result             }  } catch (error) {    ctx.send('上传失败'500, error)  }})module.exports = router.routes()

   H5部分(原生js)

<!DOCTYPE html><html><head><style>.upload-btn{  position: relative;  display: inline-block;  padding10px 20px;  background#409eff;  color: white;  border-radius4px;  cursor: pointer;  overflow: hidden;}.upload-btn input{  position: absolute;  left0;  top0;  width100%;  height100%;  opacity0;  cursor: pointer;}</style></head><body><buttonclass="upload-btn">  上传图片  <inputtype="file"accept="image/*"onchange="upload(event)"></button><br><br><imgid="preview"width="200"><script>async function upload(e){  const file = e.target.files[0]  if(!file){    alert("请选择图片")    return  }  // 限制文件类型  if(!file.type.startsWith("image/")){    alert("只能上传图片")    return  }  // URL.createObjectURL(file) 是浏览器提供的一个 原生方法,用于把 本地的 File 或 Blob 对象 转换成一个 可以在网页中访问的临时 URL,这样你就可以 不上传就预览图片或视频。  const preview = document.getElementById("preview")  preview.src = URL.createObjectURL(file)  const formData = new FormData()  formData.append("file", file)  try{    const res = await fetch("http://localhost:8912/apif/upload",{      method:"POST",      body:formData    })    const data = await res.json()    console.log("接口返回:",data)    const imgUrl = data.data    // 上传成功后替换为服务器图片    preview.src = imgUrl  }catch(err){    console.log(err)    alert("上传失败")  }}</script></body></html>

   小程序部分(uniapp)

<template>  <viewclass="container">    <!-- <image      class="haibaoDIban"      src="https://xcwh-svgall.oss-cn-wuhan-lr.aliyuncs.com/uniapp/myProject/wxcomponents/face/haibaoDIban.png"      mode="scaleToFill"    /> -->    <viewclass="center-box">        <viSwiperv-model="current"></viSwiper>    </view>        <viewclass="bgImage">    </view>    <image      class="configBtn"      src="https://xcwh-svgall.oss-cn-wuhan-lr.aliyuncs.com/uniapp/myProject/wxcomponents/face/configBtn.png"      mode="scaleToFill"      @click="chooseImage"    />      </view></template><scriptsetup>import { ref, watch } from 'vue'import viSwiper from './vi-swiper.vue'import { uploadUrl, request } from '@/api/request'const current = ref(0)const chooseImage = () => {  uni.chooseImage({    count1,    successasync function (res) {      try {        const tempFilePath = res.tempFilePaths[0]        // 1、 上传中        uni.showLoading({          title'图片上传中...',          masktrue        })        const uploadAvatar = await uni.uploadFile({          url: uploadUrl,          filePath: tempFilePath,          name'file'        })        const fileurl = JSON.parse(uploadAvatar.data).data        // 2、 人脸融合中        uni.showLoading({          title'人脸融合中...',          masktrue        })        const result = await request('/faceApi''POST', {          url: fileurl,          modelIndex: current.value        })        uni.hideLoading()        // 3、 跳转结果页        uni.navigateTo({          url`/pages/apiPage/components/faceApi/resultFeceHaibao?str=${encodeURIComponent(            result.data.FusedImage          )}`        })      } catch (err) {        console.error(err)        uni.hideLoading()        uni.showToast({          title'处理失败,请重试',          icon'none'        })      }    }  })}</script><stylescopedlang="less">.container {  width100vw;  height100vh;  position: fixed;  display: flex;  align-items: center;  justify-content: center;    .bgImage {    width100vw;    height100vh;    position: absolute;    pointer-events: none;    background-imageurl('https://xcwh-svgall.oss-cn-wuhan-lr.aliyuncs.com/uniapp/myProject/wxcomponents/face/haibaoDIban.png');    background-size100% 100%;    background-repeat: no-repeat;    background-position: center;    }}.container {  width100vw;  height100vh;  position: fixed;}.center-box {  width800rpx;  height1050rpx;}.haibaoDIban {  width100vw;  height100vh;  position: absolute;  left0;  top0;  z-index99;  pointer-events: none;}.configBtn {  width260rpx;  height: auto;  aspect-ratio874 / 346;  position: absolute;  left50%;  bottom8%;  transformtranslateX(-50%);  z-index999;}</style>

多图上传

    后端部分(koa)

const Router require('@koa/router')const router new Router()const multer require('@koa/multer')const OSS require('ali-oss')const { aliyun } = require('@/config/Account')// 使用内存存储const upload multer({ storage: multer.memoryStorage() })// 多图上传接口router.post('/upload', upload.array('files'10), async ctx => { // 最多10张  try {    const files = ctx.files    if(!files || files.length === 0) {      ctx.send([], 422'上传图片不能为空')      return    }    const client new OSS({      region: aliyun.region,      accessKeyId: aliyun.accessKeyId,      accessKeySecret: aliyun.accessKeySecret,      bucket: aliyun.bucket,      securetrue    })    const uploadedUrls = []    for(let file of files) {      const ext = file.originalname.split('.').pop()      const timestamp = Date.now()      const randomNum = Math.floor(Math.random() * 1000)      const uploadKey = `${aliyun.folder}${timestamp}${randomNum}.${ext}`      const headers = {        'Content-Disposition''inline',        'Content-Type': file.mimetype || 'image/jpg'      }      const result = await client.put(uploadKey, file.buffer, { headers })      if(result.res.statusCode === 200) {        uploadedUrls.push(result.url)      } else {        throw result      }    }    ctx.send('SUCCESS'200, uploadedUrls) // 返回数组  } catch(error) {    ctx.send('上传失败'500, error)  }})module.exports = router.routes()

   H5部分(原生js)

<!DOCTYPE html><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>多图上传示例</title><style>.upload-btn{  position: relative;  display: inline-block;  padding10px 20px;  background#409eff;  color: white;  border-radius4px;  cursor: pointer;  overflow: hidden;}.upload-btn input{  position: absolute;  left0;  top0;  width100%;  height100%;  opacity0;  cursor: pointer;}.preview-container img{  width120px;  height120px;  margin5px;  object-fit: cover;}</style></head><body><buttonclass="upload-btn">  上传图片  <inputtype="file"accept="image/*"multipleonchange="handleFiles(event)"></button><divclass="preview-container"id="previewContainer"></div><script>async function handleFiles(e){  const files = Array.from(e.target.files// 多文件  if(files.length === 0return  const previewContainer = document.getElementById('previewContainer')  previewContainer.innerHTML = ''  // 本地预览  files.forEach(file => {    if(!file.type.startsWith('image/')) return    const img = document.createElement('img')    img.src = URL.createObjectURL(file)    previewContainer.appendChild(img)  })  // 上传到后端  const uploadedUrls = []  for(let file of files){    try{      const formData = new FormData()      formData.append('files', file) // 后端字段名是 files      const res = await fetch('http://localhost:8912/apif/upload', {        method'POST',        body: formData      })      const data = await res.json()      if(data.code === 200){        uploadedUrls.push(...data.data)      } else {        alert('上传失败: ' + (data.msg || '未知错误'))      }    } catch(err){      console.error(err)      alert('上传失败')    }  }  // 上传成功后替换预览为 OSS 地址  previewContainer.innerHTML = ''  uploadedUrls.forEach(url => {    const img = document.createElement('img')    img.src = url    previewContainer.appendChild(img)  })}</script></body></html>

   小程序部分(uniapp)

<template>  <viewclass="container">    <viewclass="center-box">        <viSwiperv-model="current"></viSwiper>    </view>        <viewclass="bgImage"></view>    <image      class="configBtn"      src="https://xcwh-svgall.oss-cn-wuhan-lr.aliyuncs.com/uniapp/myProject/wxcomponents/face/configBtn.png"      mode="scaleToFill"      @click="chooseImages"    />        <!-- 多图预览 -->    <viewclass="preview-container"v-if="previews.length > 0">      <image        v-for="(src, index) in previews"        :key="index"        :src="src"        style="width:150rpx;height:150rpx;margin:5rpx;"      />    </view>  </view></template><scriptsetup>import { ref } from 'vue'import viSwiper from './vi-swiper.vue'import { uploadUrl, request } from '@/api/request'const current = ref(0)const previews = ref([]) // 本地预览或OSS地址数组const chooseImages = () => {  uni.chooseImage({    count5// 最多5张    sizeType: ['original''compressed'],    sourceType: ['album''camera'],    successasync (res) => {      try {        const tempFilePaths = res.tempFilePaths        // 先显示本地预览        previews.value = tempFilePaths        const uploadedUrls = []        // 上传每张图片        for (let path of tempFilePaths) {          uni.showLoading({ title'上传中...'masktrue })          const uploadRes = await uni.uploadFile({            url: uploadUrl,            filePath: path,            name'files' // 多图字段名,对应后端 upload.array('files')          })          const data = JSON.parse(uploadRes.data)          if (data.code === 200) {            // 如果返回是数组,这里取第一张,否则直接 push            if(Array.isArray(data.data)){              uploadedUrls.push(...data.data)            } else {              uploadedUrls.push(data.data)            }          } else {            uni.showToast({ title'上传失败'icon'none' })          }        }        // 上传完成,替换预览为OSS地址        previews.value = uploadedUrls        uni.hideLoading()        // 调用 faceApi 处理多张图片        uni.showLoading({ title'人脸融合中...'masktrue })        const result = await request('/faceApi''POST', {          urls: uploadedUrls, // 后端接口改为接收数组          modelIndex: current.value        })        uni.hideLoading()        // 跳转结果页(你可以改成显示多张融合图)        uni.navigateTo({          url`/pages/apiPage/components/faceApi/resultFeceHaibao?str=${encodeURIComponent(            result.data.FusedImage          )}`        })      } catch (err) {        console.error(err)        uni.hideLoading()        uni.showToast({ title'处理失败,请重试'icon'none' })      }    }  })}</script><stylescopedlang="less">.container {  width100vw;  height100vh;  position: fixed;  display: flex;  align-items: center;  justify-content: center;    .bgImage {    width100vw;    height100vh;    position: absolute;    pointer-events: none;    background-imageurl('https://xcwh-svgall.oss-cn-wuhan-lr.aliyuncs.com/uniapp/myProject/wxcomponents/face/haibaoDIban.png');    background-size100% 100%;    background-repeat: no-repeat;    background-position: center;    }}.center-box {  width800rpx;  height1050rpx;}.configBtn {  width260rpx;  height: auto;  aspect-ratio874 / 346;  position: absolute;  left50%;  bottom8%;  transformtranslateX(-50%);  z-index999;}.preview-container {  position: absolute;  bottom20%;  left50%;  transformtranslateX(-50%);  display: flex;  flex-wrap: wrap;}</style>
本站文章均为手工撰写未经允许谢绝转载:夜雨聆风 » 源码顶级干货 | 本地图片直传服务器

评论 抢沙发

6 + 9 =
  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址
×
订阅图标按钮