文件上传与下载

最近在项目中用到了文件上传和下载,遇到了一些问题。例如利用组件上传文件时,同时提交文件和表单数据;下载时用post方法;利用a标签download属性失效等问题,查看了一些资料,觉得有必要系统的总结一下,当是梳理一下关于文件操作零碎的知识点。

一,文件上传

  1,基础知识

  1,form表单提交文件

  enctype 属性:

       application/x-www-form-urlencoded:只处理表单域中的value属性值,采用这种编码方式的表单会将表单域的值处理成url编码方式,默认方式

          multipart/form-data:这种编码方式将表单中的数据变成二进制数据进行上传,不对字符编码,可以实现多种类型的文件上传

       text/plain:纯文本传输,不含任何控件和格式字符,这种方式主要适用于直接通过表单发送邮件的方式

  MIME类型:MIME类型就是设定某种扩展名的文件用一种应用程序来打开的方式类型,当该扩展名文件被访问的时候,浏览器会自动使用指定应用程序来打开。

        多用于指定一些客户端自定义的文件名,以及一些媒体文件打开方式

  文本文件:由可见字符组成的文件。所谓可见字符是指ASCII码为32到126的字符、回车符(ASCII码13)、换行符(ASCII码10)、制表符(ASCII码9)、以及所有汉字字符                (当然也包括其他字符集如韩文、日文、阿拉伯文等等)。如果是Unicode文本,则还包括ASCII码0

  二进制文件::以0和1的形式存储在硬盘的所有电脑文件,可以说所有的存储在电脑上的文件均为二进制文件

  以上两种文件只是再逻辑上区分,物理上其实都属于二进制文件

 

  文件上传:设置enctype="multipart/form-data",method为post。当提交表单后浏览器将表单数据封装成http请求格式发送到服务器,服务器端收到"multipart/form-data"类型的                             http请求消息,读取这个请求消息里面的实体内容

   2,传统表单上传文件

  <form action="文件上传的服务器地址" method="post" enctype="multipart/form-data">

       <input type="file" name="file" value="选择jar包"/>

       <input id="submit_form" type="submit" class="btn btn-success save" value="保存"/>

  </form>

  1.服务器端程序收到"multipart/form-data"类型的http请求消息

  2.读取这个请求消息里面的实体内容

  3.解析每个分区的数据

  4.从每个分区中解析出描述头和主体内容部分

  表单上传注意点:

  1, method="post": 采用post方式提交数据

  2, enctype="multipart/form- data":采用multipart格式上传文件,此时request头会显示

    Content-Type:multipart/form-data; boundary=— WebKitFormBoundaryzr34cwJ67R95KQC9

  3, action:标明上传的服务端处理地址

  4, type="file":使用input的file控件上传

  5, 如果是多文件批量上传,将input加上 multiple属性(支持按住shift键多选文件)

  6, accept属性是HTML5的新属性,它规定了可通过文件上传提交的文件类型

  7, 上传的触发事件可以是:input[type=”file”]的onChange触发,也可以由一个独立的按钮的onClick使整个表单提交,此时还可以用input[type="hidden"]带一些其它的参数,比如              Token来源验证等等。

 

    3,AJAX无刷新上传

  <form>

      <input id="file" name="file" type="file" />

      <input id="token" name="token" type="hidden" />

  </form>

  $("#file").on("change", function(){

    var formData = new FormData();

    formData.append("file", $("#file")[0].files);

    formData.append("token", $("#token").val());

    $.ajax({

        url: "http://uploadUrl",

        type: "POST",

        data: formData,

        processData: false,     // 不要对data参数进行序列化处理,默认为true

        contentType: false,      // 不要设置Content-Type请求头,因为文件数据是以 multipart/form-data 来编码

        success: function(response){

              // 根据返回值来操作

        }

    });

  });

    4,在form中使用antd的Upload组件和其他表单数据一起提交(借鉴网上的代码)

思路:在Upload组件中定义beforeUpload方法并且返回false拦截文件的自动上传,同时把文件信息添加到state中,由于文件列表可以删除影响state,所以需要在Upload组件中添加onRemove方法用于在删除列表时候实时更新state

  

//这个是监听文件变化的

fileChange=(params)=>{

    const {file,fileList}=params;

    if(file.status==='uploading'){

        setTimeout(()=>{

            this.setState({

                percent:fileList.percent   

            })

        },1000)       

    }

}

// 拦截文件上传

beforeUploadHandle=(file)=>{

    this.setState(({fileData})=>({

        fileData:[...fileData,file],

    }))

    return false;

}

// 文件列表的删除

fileRemove=(file)=>{

    this.setState(({fileData})=>{

        const index=fileData.indexOf(file);

        const newFileList=fileData.slice();

        newFileList=splice(index,1);

        return {

           fileData:newFileList

        }

    })

}

render(){

    <FormItem labelCol={{span:5}} wrapperCol={{span:15}} label='文件上传'>

        {getFieldDecorator(form,settings,formName,'name',values)(

            <Upload action='路径' beforUpload={this.beforeUploadHandle} onChange={this.fileChange} onRemove={this.fileRemove} fileList={this.state.fileData}>

                <Button><Icon type='upload' />上传文件</Button>

            </Upload>

        )}

    </FormItem>

通过append方法将数据逐条添加到formData中

const {fileData}=this.state;

const formData=new formData();

fileData.forEach((file)=>{

    formData.append('files',file);

})

// fields为表单其他项的数据,在antd-pro中是fileds

Object.keys(fields).map((item)=>{

    formData.append(item,fields[item]);

})

this.props.dispatch({

    type:'pluginInfo/upload',

    payload:formData,

    callback:()=>{

        resetFields();

        this.setState({

            fileData:[],

        })

        this.props.handleModalVisible(false)//这个是项目中的关闭弹窗方法,可以无视

    }

})

 

 

二,文件下载

首先,要清楚实现文件下载有哪几种方式,自己总结了几种在前后端分离的项目中用过方式,通过查资料书面的整理了如下:

1,后端给了一个下载的api请求接口,该接口返回文件的内容(流的形式),前端拿到文件的内容然后写进文件,实现下载。

  fetch('/api/resource/exportExcelResource', {

  // body: JSON.stringify({...param}), 如果需要可以向后台传参,此时method为'POST'

  method: 'GET',

  }).then(response => {

  return response.blob();   //请求接口返回文件二进制流,转成blob对象,并将内容写入文件

  }).then(blob => {

  const url = window.URL.createObjectURL(blob);//创建一个存储在内存中的新的url

  const a = document.createElement('a');

  a.href = url;

  a.download = 'XXX文件.xls';

  a.click();

 })

2,后端直接给了文件在服务器上的绝对地址,根据该地址直接下载

  <a href="http://172.XX.XX.XX/文件1.xls"></a>

  也可以通过发送请求,来获取二进制文件流,然后按照方法一,前端写入文件进行下载操作

  fetch('http://172.XX.XX.XX/文件1.xls', {

  // body: JSON.stringify({...param}), 如果需要可以向后台传参,此时method为'POST'

  method: 'GET',

  }).then(response => {

  return response.blob();   //请求接口返回文件二进制流,转成blob对象,并将内容写入文件

  }).then(blob => {

  const url = window.URL.createObjectURL(blob);//创建一个存储在内存中的新的url

  const a = document.createElement('a');

  a.href = url;

  a.download = 'XXX文件.xls';    //文件名可以后端给出,前端用response.headers.get('Content-Disposition')从响应头的Content-Disposition中拿到filename

    a.click();

  })

  另外还可通过iframe来实现下载:

  let elem = document.createElement('iframe');

  elem.src = url;

  elem.style.display = 'none';

  document.body.appendChild(elem);

 

以上是我用到的两种文件下载方式,此处存在的问题就是:当文件下载地址和当前应用地址不是同源URL时,a标签的download属性会失效,因为download属性是告诉浏览器将要下载该文件,而不只是访问文件,所以必须该属性必须在同源URL下才生效。同时当http头中content-disposition设置了文件下载名称时,会覆盖a标签download属性设置的下载名称

当使用到a标签的download属性失效时解决办法有两种:

    1,在服务器上配置nginx代理,通过同源URL重定向到文件地址。

    2,让后端获取文件,直接将文件内容通过接口发送给前端,前端再使用方式1实现下载

 

至于文件的导出,其实和文件下载有相同之处,只不过文件导出需要访问后台接口进行筛选出需要导出的数据,生成Excel再发送到前端,前端就实现和下载相同的操作。

 

转载于:https://www.cnblogs.com/mikoBlog/p/10235758.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值