Vue+NodeJS实现大文件上传的示例代码

目录
  • 整体思路
  • 项目演示
    • 前端界面
    • 文件切片
    • hash计算
    • 查询切片状态
    • 切片上传(断点续传)
    • 文件总体上传进度
    • 合并文件
  • 优化
    • 请求并发数控制
    • hash值计算优化

常见的文件上传方式可能就是new一个FormData,把文件append进去以后post给后端就可以了。但如果采用这种方式来上传大文件就很容易产生上传超时的问题,而且一旦失败还得从新开始,在漫长的等待过程中用户还不能刷新浏览器,不然前功尽弃。因此这类问题一般都是通过切片上传。

整体思路

  • 将文件切成多个小文件
  • hash计算,需要计算一个文件的唯一标识,这样下次再传,就能筛选出剩余的切片进行上传。
  • 所有切片上传后,通知服务端进行切片合成
  • 上传成功通知前端文件路径
  • 整个过程如果出现失败,下次再传时,由于之前计算过文件hash,可以筛选出未传数的切片续传(断点续传); 如果整个文件已经上传过,就不需要传输(秒传)

项目演示

这里用vue和node分别搭建前端和后端

前端界面

fileUpload.vue

<template>
    <div class="wrap">
        <div >
           <el-upload
             ref="file"
             :http-request="handleFileUpload"
             action="#"
             class="avatar-uploader"
             :show-file-list='false'
           >
               <el-button type="primary">上传文件</el-button>
           </el-upload>
           <div>
               <div>计算hash的进度:</div>
              <el-progress :stroke-width="20" :text-inside="true" :percentage="hashProgress"></el-progress>
           </div>
           <div>
               <div>上传进度:</div>
              <el-progress :stroke-width="20" :text-inside="true" :percentage="uploaedProgress"></el-progress>
           </div>
        </div>
    </div>
</template>

文件切片

利用 File.prototype.slice 的方法可以对文件进行切片 fileUpload.vue

    const CHUNK_SIZE=1024*1024//每个切片为1M
    import sparkMD5 from 'spark-md5'
    export default {
        name:'file-upload',
        data(){
            return {
              file:null,//上传的文件
              chunks:[],//切片
              hashProgress:0,//hash值计算进度
              hash:''
            }
        },
        methods:{
            async handleFileUpload(e){
              if(!file){
                  return
              }
              this.file=file
              this.upload()
            },
            //文件上传
            async upload(){
             //切片
             const chunks=this.createFileChunk(this.file)
            //...
            //hash计算
              const hash=await this.calculateHash1(chunks)
            }
           },
            //文件切片
             createFileChunk(size=CHUNK_SIZE){
              const chunks=[];
              let cur=0;
              const maxLen=Math.ceil(this.file.size/CHUNK_SIZE)
              while(cur<maxLen){
                 const start=cur*CHUNK_SIZE;
                 const end = ((start + CHUNK_SIZE) >= this.file.size) ? this.file.size : start + CHUNK_SIZE;
                 chunks.push({index:cur,file:this.file.slice(start,end)})
                 cur++
              }
              return chunks
            },

    }

hash计算

利用md5可以计算出文件唯一的hash值

这里可以使用 spark-md5 这个库可以增量计算文件的hash值

calculateHash1(chunks){
   const spark=new sparkMD5.ArrayBuffer()
   let count =0
   const len=chunks.length
   let hash
   const self=this
   const startTime = new Date().getTime()
   return new Promise((resolve)=>{
        const loadNext=index=>{
            const reader=new FileReader()
            //逐片读取文件切片
            reader.readAsArrayBuffer(chunks[index].file)
            reader.onload=function(e){
                const endTime=new Date().getTime()
                chunks[count]={...chunks[count],time:endTime-startTime}
                count++
                //读取成功后利用spark做增量计算
                spark.append(e.target.result)
                if(count==len){
                    self.hashProgress=100
                    //返回整个文件的hash
                     hash=spark.end()
                     resolve(hash)
                }else{
                    //更新hash计算进度
                    self.hashProgress+=100/len
                    loadNext(index+1)
                }
            }
        }
        loadNext(0)
   })

},

可以看到整个过程还是比较费时间的,有可能会导致UI阻塞(卡),因此可以通过webwork等手段优化这个过程,这点我们放在最后讨论

查询切片状态

在知道了文件的hash值以后,在上传切片前我们还要去后端查询下文件的上传状态,如果已经上传过,那就没有必要再上传,如果只上传了一部分,那就上传还没有上过过的切片(断点续传)

前端 fileUpload.vue

//...
methods:{
//...
async upload(){
//...切片,计算hash
this.hash=hash
//查询是否上传 将hash和后缀作为参数传入
this.$http.post('/checkfile',{
  hash,
  ext:this.file.name.split('.').pop()
})
.then(res=>{
  //接口会返回两个值 uploaded:Boolean 表示整个文件是否上传过 和 uploadedList 哪些切片已经上传
   const {uploaded,uploadedList}=res.data
   //如果已经上传过,直接提示用户(秒传)
    if(uploaded){
        return  this.$message.success('秒传成功')
    }
  //这里我们约定上传的每个切片名字都是 hash+‘-'+index
    this.chunks=chunks.map((chunk,index)=>{
        const name=hash+'-'+index
        const isChunkUploaded=(uploadedList.includes(name))?true:false//当前切片是否有上传
        return {
            hash,
            name,
            index,
            chunk:chunk.file,
            progress:isChunkUploaded?100:0//当前切片上传进度,如果有上传即为100 否则为0,这是用来之后计算总体上传进度
        }
    })
    //上传切片
    this.uploadChunks(uploadedList)
  })
 }

}

文件切片 this.chunks

服务端 server/index.js

const Koa=require('koa')
const Router=require('koa-router')
const koaBody = require('koa-body');
const path=require('path')
const fse=require('fs-extra')
const app=new Koa()
const router=new Router()
//文件存放在public下
const UPLOAD_DIR=path.resolve(__dirname,'public')
app.use(koaBody({
    multipart:true, // 支持文件上传
}));

router.post('/checkfile',async (ctx)=>{
    const body=ctx.request.body;
    const {ext,hash}=body
    //合成后的文件路径 文件名 hash.ext
    const filePath=path.resolve(UPLOAD_DIR,`${hash}.${ext}`)
    let uploaded=false
    let uploadedList=[]
    //判断文件是否已上传
    if(fse.existsSync(filePath)){
      uploaded=true
    }else{
    //所有已经上传过的切片被存放在 一个文件夹,名字就是该文件的hash值
      uploadedList=await getUploadedList(path.resolve(UPLOAD_DIR,hash))
    }
    ctx.body={
      code:0,
      data:{
        uploaded,
        uploadedList
      }
    }
})

async function getUploadedList(dirPath){
//将文件夹中的所有非隐藏文件读取并返回
   return fse.existsSync(dirPath)?(await fse.readdir(dirPath)).filter(name=>name[0]!=='.'):[]
}

切片上传(断点续传)

再得知切片上传状态后,就能筛选出需要上传的切片来上传。 前端 fileUpload.vue

uploadChunks(uploadedList){
 //每一个要上传的切片变成一个请求
  const requests=this.chunks.filter(chunk=>!uploadedList.includes(chunk.name))
  .map((chunk,index)=>{
     const form=new FormData()
     //所有上传的切片会被存放在 一个文件夹,文件夹名字就是该文件的hash值 因此需要hash和name
     form.append('chunk',chunk.chunk)
     form.append('hash',chunk.hash)
     form.append('name',chunk.name)
     //因为切片不一定是连续的,所以index需要取chunk对象中的index
     return {form,index:chunk.index,error:0}
  })//所有切片一起并发上传
  .map(({form,index})=>{
      return this.$http.post('/uploadfile',form,{
          onUploadProgress:progress=>{
              this.chunks[index].progress=Number(((progress.loaded/progress.total)*100).toFixed(2)) //当前切片上传的进度
          }
      })
  })

  Promise.all(requests).then((res)=>{
   //所有请求都成功后发送请求给服务端合并文件
    this.mergeFile()
  })

},

服务端

router.post('/uploadfile',async (ctx)=>{
  const body=ctx.request.body
  const file=ctx.request.files.chunk
  const {hash,name}=body
 //切片存放的文件夹所在路径
  const chunkPath=path.resolve(UPLOAD_DIR,hash)
  if(!fse.existsSync(chunkPath)){
      await fse.mkdir(chunkPath)
  }
 //将文件从临时路径里移动到文件夹下
  await fse.move(file.filepath,`${chunkPath}/${name}`)

  ctx.body={
    code:0,
    message:`切片上传成功`
   } 

})

上传后切片保存的位置

文件总体上传进度

总体上传进度取决于每个切片上传的进度和文件总体大小,可以通过计算属性来实现

fileUpload.vue

uploaedProgress(){
    if(!this.file || !this.chunks.length){
        return 0
    }
    //累加每个切片已上传的部分
   const loaded =this.chunks.map(chunk=>{
       const size=chunk.chunk.size
       const chunk_loaded=chunk.progress/100*size
       return chunk_loaded
    }).reduce((acc,cur)=>acc+cur,0)

   return parseInt(((loaded*100)/this.file.size).toFixed(2))
},

合并文件

前端 fileUpload.vue

//要传给服务端文件后缀,切片的大小和hash值
mergeFile(){
    this.$http.post('/mergeFile',{
        ext:this.file.name.split('.').pop(),
        size:CHUNK_SIZE,
        hash:this.hash
    }).then(res=>{
        if(res && res.data){
            console.log(res.data)
        }
    })
},

服务端

router.post('/mergeFile',async (ctx)=>{
  const body=ctx.request.body
  const {ext,size,hash}=body
  //文件最终路径
  const filePath=path.resolve(UPLOAD_DIR,`${hash}.${ext}`)
  await mergeFile(filePath,size,hash)
  ctx.body={
     code:0,
     data:{
         url:`/public/${hash}.${ext}`
     }
  }
})

async function mergeFile(filePath,size,hash){
  //保存切片的文件夹地址
  const chunkDir=path.resolve(UPLOAD_DIR,hash)
  //读取切片
  let chunks=await fse.readdir(chunkDir)
  //切片要按顺序合并,因此需要做个排序
  chunks=chunks.sort((a,b)=>a.split('-')[1]-b.split('-')[1])
  //切片的绝对路径
  chunks=chunks.map(cpath=>path.resolve(chunkDir,cpath))
  await mergeChunks(chunks,filePath,size)
}

//边读边写至文件最终路径
function mergeChunks(files,dest,CHUNK_SIZE){
  const pipeStream=(filePath,writeStream)=>{
    return new Promise((resolve,reject)=>{
        const readStream=fse.createReadStream(filePath)
        readStream.on('end',()=>{
            //每一个切片读取完毕后就将其删除
            fse.unlinkSync(filePath)
            resolve()
        })
        readStream.pipe(writeStream)
    })

  }

  const pipes=files.map((file,index) => {
  return pipeStream(file,fse.createWriteStream(dest,{
        start:index*CHUNK_SIZE,
        end:(index+1)*CHUNK_SIZE
    }))
  });
  return Promise.all(pipes)

}

大文件切片上传的功能已经实现,让我们来看下效果(这里顺便展示一下单个切片的上传进度)

可以看到由于大量的切片请求并发上传,虽然浏览器本身对同时并发的请求数有所限制(可以看到许多请求是pending状态),但还是造成了卡顿,因此这个流程还是需要做一个优化

优化

请求并发数控制

fileUpload.vue

逐片上传

这也是最直接的一种做法,可以看作是并发请求的另一个极端,上传成功一个再上传第二个,这里还要处理一下错误重试,如果连续失败3次,整个上传过程终止

uploadChunks(uploadedList){
  console.log(this.chunks)
  const requests=this.chunks.filter(chunk=>!uploadedList.includes(chunk.name))
  .map((chunk,index)=>{
     const form=new FormData()
     form.append('chunk',chunk.chunk)
     form.append('hash',chunk.hash)
     form.append('name',chunk.name)
     return {form,index:chunk.index,error:0}
  })
//   .map(({form,index})=>{
//       return this.$http.post('/uploadfile',form,{
//           onUploadProgress:progress=>{
//               this.chunks[index].progress=Number(((progress.loaded/progress.total)*100).toFixed(2))
//           }
//       })
//   })
// //   console.log(requests)
//   Promise.all(requests).then((res)=>{
//     console.log(res)
//     this.mergeFile()
//   })

  const sendRequest=()=>{
      return new Promise((resolve,reject)=>{
            const upLoadReq=(i)=>{
                const req=requests[i]
                const {form,index}=req
                this.$http.post('/uploadfile',form,{
                    onUploadProgress:progress=>{
                        this.chunks[index].progress=Number(((progress.loaded/progress.total)*100).toFixed(2))
                    }
                })
                .then(res=>{
                    //最后一片上传成功,整个过程完成
                    if(i==requests.length-1){
                        resolve()
                        return
                    }
                    upLoadReq(i+1)
                })
                .catch(err=>{
                    this.chunks[index].progress=-1
                    if(req.error<3){
                        req.error++
                        //错误累加后重试
                        upLoadReq(i)
                    }else{
                        reject()
                    }
                })
           }
           upLoadReq(0)

      })

  }
  //整个过程成功后再合并文件
  sendRequest()
  .then(()=>{
     this.mergeFile()
  })

},

可以看到每次只有一个上传请求

最终生成的文件

多个请求并发

逐个请求的确是可以解决卡顿的问题,但是效率有点低,我们还可以在这个基础上做到有限个数的并发

一般这种问题的思路就是要形成一个任务队列,开始的时候先从requests中取出指定并发数的请求对象(假设是3个)塞满队列并各自开始请求任务,每一个任务结束后将该任务关闭退出队列然后再从request说中取出一个元素加入队列并执行,直到requests清空,这里如果某一片请求失败的话那还要再塞入request队首,这样下次执行时还能从这个请求开始达到了重试的目的

async uploadChunks(uploadedList){
  console.log(this.chunks)
  const requests=this.chunks.filter(chunk=>!uploadedList.includes(chunk.name))
  .map((chunk,index)=>{
     const form=new FormData()
     form.append('chunk',chunk.chunk)
     form.append('hash',chunk.hash)
     form.append('name',chunk.name)
     return {form,index:chunk.index,error:0}
  })

const sendRequest=(limit=1,task=[])=>{
    let count=0 //用于记录请求成功次数当其等于len-1时所有切片都已上传成功
    let isStop=false //标记错误情况,如果某一片错误数大于3整个任务标记失败 并且其他并发的请求凭次标记也不在递归执行
    const len=requests.length
    return new Promise((resolve,reject)=>{
            const upLoadReq=()=>{
                if(isStop){
                    return
                }
                const req=requests.shift()
                if(!req){
                    return
                }
                const {form,index}=req
                this.$http.post('/uploadfile',form,{
                    onUploadProgress:progress=>{
                        this.chunks[index].progress=Number(((progress.loaded/progress.total)*100).toFixed(2))
                    }
                })
                .then(res=>{
                    //最后一片
                    if(count==len-1){
                    resolve()
                    }else{
                    count++
                    upLoadReq()
                    }
                })
                .catch(err=>{
                    this.chunks[index].progress=-1
                    if(req.error<3){
                        req.error++
                        requests.unshift(req)
                        upLoadReq()
                    }else{
                        isStop=true
                        reject()
                    }
                })
            }

            while(limit>0){
              //模拟形成了一个队列,每次结束再递归执行下一个任务
              upLoadReq()
              limit--
            }
    })

}

   sendRequest(3).then(res=>{
      console.log(res)
      this.mergeFile()
   })

},

hash值计算优化

除了请求并发需要控制意外,hash值的计算也需要关注,虽然我们采用了增量计算的方法,但是可以看出依旧比较费时,也有可能会阻塞UI

webWork

这相当于多开了一个线程,让hash计算在新的线程中计算,然后将结果通知会主线程

calculateHashWork(chunks){
   return new Promise((resolve)=>{
      //这个js得独立于项目之外
      this.worker=new worker('/hash.js')
      //切片传入现成
      this.worker.postMessage({chunks})
      this.worker.onmessage=e=>{
      //线程中返回的进度和hash值
       const {progress,hash}=e.data
       this.hashProgress=Number(progress.toFixed(2))
       if(hash){
        resolve(hash)

       }

     }
   })
},

hash.js

//独立于项目之外,得单独

// 引入spark-md5

self.importScripts('spark-md5.min.js')

self.onmessage = e=>{
  // 接受主线程传递的数据,开始计算
  const {chunks } = e.data
  const spark = new self.SparkMD5.ArrayBuffer()

  let progress = 0
  let count = 0

  const loadNext = index=>{
    const reader = new FileReader()
    reader.readAsArrayBuffer(chunks[index].file)
    reader.onload = e=>{
      count ++
      spark.append(e.target.result)

      if(count==chunks.length){
      //向主线程返回进度和hash
        self.postMessage({
          progress:100,
          hash:spark.end()
        })
      }else{
        progress += 100/chunks.length
        //向主线程返回进度
        self.postMessage({
          progress
        })
        loadNext(count)
      }
    }
  }
  loadNext(0)
}

时间切片

还有一种做法就是借鉴react fiber架构,可以通过时间切片的方式在浏览器空闲的时候计算hash值,这样浏览器的渲染是联系的,就不会出现明显卡顿

calculateHashIdle(chunks){
    return new Promise(resolve=>{
        const spark=new sparkMD5.ArrayBuffer()
        let count=0
        const appendToSpark=async file=>{
            return new Promise(resolve=>{
                const reader=new FileReader()
                reader.readAsArrayBuffer(file)
                reader.onload=e=>{
                    spark.append(e.target.result)
                    resolve()
                }
            })
        }

        const workLoop=async deadline=>{
        //当切片没有读完并且浏览器有剩余时间
            while(count<chunks.length && deadline.timeRemaining()>1){
                await appendToSpark(chunks[count].file)
                count++
                if(count<chunks.length){
                    this.hashProgress=Number(((100*count)/chunks.length).toFixed(2))
                }else{
                    this.hashProgress=100
                    const hash=spark.end()
                    resolve(hash)
                }
            }
            window.requestIdleCallback(workLoop)
        }
        window.requestIdleCallback(workLoop)
    })
}

以上就是Vue+NodeJS实现大文件上传的示例代码的详细内容,更多关于Vue NodeJS大文件上传的资料请关注我们其它相关文章!

(0)

相关推荐

  • React+Node实现大文件分片上传、断点续传秒传思路

    目录 1.整体思路 2.实现步骤 2.1 文件切片加密 2.2 查询上传文件状态 2.3 秒传 2.4 上传分片.断点续传 2.5 合成分片还原完整文件 3.总结 4.后续扩展与思考 5.源码 1.整体思路 将文件切成多个小的文件: 将切片并行上传: 所有切片上传完成后,服务器端进行切片合成: 当分片上传失败,可以在重新上传时进行判断,只上传上次失败的部分实现断点续传: 当切片合成为完整的文件,通知客户端上传成功: 已经传到服务器的完整文件,则不需要重新上传到服务器,实现秒传功能: 2.实现步骤

  • Vue 大文件上传和断点续传的实现

    文件上传的 2 套方案 基于文件流(form-data) element-ui 框架的上传组件,就是默认基于文件流的. 数据格式:form-data: 传递的数据: file 文件流信息:filename 文件名字 客户端把文件转换为 base64 通过 fileRead.readAsDataURL(file) 转为 base64 字符串后,要用 encodeURIComponent 编译再发送, 发送的数据经由 qs.stringify 处理,请求头添加 "Content-Type"

  • 基于Node.js的大文件分片上传示例

    我们在做文件上传的时候,如果文件过大,可能会导致请求超时的情况.所以,在遇到需要对大文件进行上传的时候,就需要对文件进行分片上传的操作.同时如果文件过大,在网络不佳的情况下,如何做到断点续传?也是需要记录当前上传文件,然后在下一次进行上传请求的时候去做判断. 先上代码:代码仓库地址 前端 1. index.html <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8

  • Vue+Node实现大文件上传和断点续传

    目录 源代码 Blob.slice 切片 初始化文件内容 FormData.append() 大文件上传 断点续传 代码 创建切片 源码 worker 线程通讯的逻辑 断点续传 秒传 源代码 断点续传.分片上传.秒传.重试机制 文件上传是开发中的难点, 大文件上传及断点续传 难点中的细节及核心技术点. element-ui 框架的上传组件,是默认基于文件流的. 数据格式:form-data: 传递的数据: file 文件流信息:filename 文件名字 通过 fileRead.readAsDa

  • Node.js + express实现上传大文件的方法分析【图片、文本文件】

    本文实例讲述了Node.js + express实现上传大文件的方法.分享给大家供大家参考,具体如下: 对于大文件的上传我们首先要引入一个叫做 multer 的库: npm install --save multer 关于这个库,大家可以查阅官方文档: 点击跳转 https://www.npmjs.com/package/multer 我们先将库引入我们的项目中: var multer = require('multer') var upload = multer({ dest: 'upload

  • Vue+NodeJS实现大文件上传的示例代码

    目录 整体思路 项目演示 前端界面 文件切片 hash计算 查询切片状态 切片上传(断点续传) 文件总体上传进度 合并文件 优化 请求并发数控制 hash值计算优化 常见的文件上传方式可能就是new一个FormData,把文件append进去以后post给后端就可以了.但如果采用这种方式来上传大文件就很容易产生上传超时的问题,而且一旦失败还得从新开始,在漫长的等待过程中用户还不能刷新浏览器,不然前功尽弃.因此这类问题一般都是通过切片上传. 整体思路 将文件切成多个小文件 hash计算,需要计算一

  • vue中实现图片和文件上传的示例代码

    html页面 <input type="file" value="" id="file" @change='onUpload'>//注意不能带括号 js代码 methods: { //上传图片 onUpload(e){ var formData = new FormData(); f ormData.append('file', e.target.files[0]); formData.append('type', 'test');

  • SpringBoot整合腾讯云COS对象存储实现文件上传的示例代码

    目录 1.开通腾讯云对象存储服务 2.创建存储桶 3.密钥管理,新建密钥 4.yml配置密钥.COS信息 5.COSConfig配置类 6.COS文件上传工具类 7.Controller测试上传接口: 8.PostMan接口调用 9.浏览器预览效果 企业级项目开发中都会有文件.图片.视频等文件上传并能够访问的场景,对于初学者Demo可能会直接存储在应用服务器上:对于传统项目可能会单独搭建FastDFS.MinIO等文件服务来实现存储,这种方案可能对于企业成本较小,但缺点也是很多,例如:1.增加技

  • Vue项目实现html5图片上传的示例代码

    目录 图例 1.选择图片 2.预览图片 2.1添加图片预览代码 两种方法的对比 3.裁剪图片 4.上传 选择图片 -> 预览图片 -> 裁剪图片 -> 上传图片 我会以事例贯穿图片接下来,就详细的介绍每个步骤具体实现. 图例 1.选择图片 选择图片有什么好讲的呢?不就一个 input[type=file] ,然后点击就可以了吗?确实是这样的,但是,我们想要做得更加的友好一些,比如需要过滤掉非图片文件, 或只允许从摄像头拍照获取图片等,还是需要进行一些简单配置的. 下面就先来看看最简单的选

  • Java实现一个简单的文件上传案例示例代码

    Java实现一个简单的文件上传案例 实现流程: 1.客户端从硬盘读取文件数据到程序中 2.客户端输出流,写出文件到服务端 3.服务端输出流,读取文件数据到服务端中 4.输出流,写出文件数据到服务器硬盘中 下面上代码 上传单个文件 服务器端 package FileUpload; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Serve

  • GO语言实现文件上传的示例代码

    目录 前言 文件上传 表单操作 服务端操作 流程实现 小结 前言 最近在写一个文件上传的功能,现在来进行整理总结一下go语言如何上传文件的,本文主要分享一下golang实现文件上传的流程和具体代码,供大家参考,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助. 文件上传 表单操作 要使表单能够上传文件,需要添加form的enctype属性enctype="multipart/form-data",upload.html代码如下: <html> <head> &

  • asp.net大文件上传解决方案实例代码

    以ASP.NET Core WebAPI 作后端 API ,用 Vue 构建前端页面,用 Axios 从前端访问后端 API ,包括文件的上传和下载. 准备文件上传的API #region 文件上传 可以带参数 [HttpPost("upload")] public JsonResult uploadProject(IFormFile file, string userId) { if (file != null) { var fileDir = "D:\\aaa"

  • 利用discuz实现PHP大文件上传应用实例代码

    对于确实需要改善论坛附件上传条件的朋友可以尝试将上面提及的参数在php.ini进行设置,以适应大文件上传的需要.同时别忘记在论坛的后台相应做附件限制的地方进行设置. 论坛主要有2个地方可以对附件上传的大小进行限制,级别从高到低依次为: 帖子相关---附件类型尺寸 用户组---附件相关 同时,下面提供一个配置指导,来源一些成功通过http上传大附件的朋友的提供,当然,由于大家的服务器配置情况以及网络情况不同,并不一定适用你的情况,可能很多地方需要参照修改: 打开php.ini, 参数 设置 说明

  • java接收ios文件上传的示例代码

    本文实例为大家分享了java如何接收ios文件上传的具体代码,供大家参考,具体内容如下 ios Multipart/form-data POST请求java后台spring接口一直出错,搞了两天,终于解决了,积累下来 package com.xx.controller; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.util.Iterator

随机推荐