java领域有两个常用的文件上传项目:Common-FileUpload和COS。struts2则在原来的文件上传的项目基础上,进行进一步的封装,从而进一步地简化了文件上传。除此之外,struts2对文件下载支持stream的结果类型,通过借助于struts2提供的文件下载支持,应用可以实现非西欧字符文件名的文件下载,并可以在文件下载前检查用户的权限,从而通过授权控制来控制文件的下载。
文件上传原理
表单元素的enctype属性
大部分时候,无需设置表单元素的enctype属性,我们只设置表单的action属性和method属性,其中action属性指定了表单提交到的URL,而method属性指定是以POST还是GET方式提交请求。
表单的enctype属性指定的是表单数据的编码方式,该属性有如下3个值:
application/x-www-form-urlencoded:这是默认的编码方式,它只处理表单域里的value属性值,采用这种编码方式的表单会将表单域的值处理成URL编码方式。
multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数里。
text/plain,这种编码方式当表达的action属性为mailto:URL的形式时比较方便,这种方式主要使用与直接通过表单发送邮件的方式。
1使用Common-FileUpload框架上传文件
这个框架是Apache组织下的jakarta-commons项目组下的一个小项目,该框架可以方便的将multipart/form-data类型请求中的各种表单域解析出来,该项目还依赖于另一个项目:Common-IO
登录以下站点下载commons-fileupload-1.2.1将lib\commons-fileupload-1.2.1.jar复制到WEB应用的WEB-INF\lib目录下。
http://commons.apache.org/fileupload/download_fileupload.cgi
然后再将commons-io-1.3.2.jar也复制到WEB应用的WEB-INF\lib目录下。
//下面是通过Common-FileUpload框架进行文件上传的jsp页面代码:
struts2默认使用Jakarta的Common-FileUpload的文件上传解析器。
struts.multipart.parser=jakarta
文件上传页面
文件上传页面只包含两个表单域:文件标题和文件浏览域。当然为了实现文件上传,应该将这两个表单域所在表单的enctype属性设置为"multipart/form-data"。页面代码如下;
<%@ page language="java" contentType="text/html;Charset=GBK"%>
<html>
<head>
<title>简单文件上传</title>
</head>
<body>
<form action="upload.action" method="post" enctype="multipart/form-data">
文件标题:<input type="text" name="title"/><br/>
选择文件:<input type="file" name="upload"/><br/>
<input value="上传" type="submit"/>
</form>
</body>
</html>
Struts2使用File类型来封装文件域,下面是处理上传请求的Action类代码:
package lee;
import java.io.*;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
public class uploadAction extends ActionSupport
{
//封装文件标题请求参数的属性
private String title;
//封装上传文件域的属性
private File upload;
//封装上传文件类型的属性
private String uploadContentType;
//封装上传文件名的属性
private String uploadFileName;
//接受依赖注入的属性
private String savePath;
//接受依赖注入的方法
public void setSavePath(String value)
{
this.savePath = value;
}
//返回上传文件的保存位置
private String getSavePath() throws Exception
{
return ServletActionContext.getRequest().getRealPath(savePath);
}
//文件标题的setter和getter方法
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return (this.title);
}
//上传文件对应文件内容的setter和getter方法
public void setUpload(File upload)
{
this.upload = upload;
}
public File getUpload()
{
return (this.upload);
}
//上传文件的文件类型的setter和getter方法
public void setUploadContentType(String uploadContentType)
{
this.uploadContentType = uploadContentType;
}
public String getUploadContentType()
{
return (this.uploadContentType);
}
//上传文件的文件名的setter和getter方法
public void setUploadFileName(String uploadFileName)
{
this.uploadFileName = uploadFileName;
}
public String getUploadFileName()
{
return (this.uploadFileName);
}
@Override
public String execute() throws Exception
{
//以服务器的文件保存地址和原文件名建立上传文件输出流
FileOutputStream fos = new FileOutputStream(getSavePath() + "\\" + getUploadFileName());
//以上传文件建立一个文件上传流
FileInputStream fis = new FileInputStream(getUpload());
//将上传文件的内容写入服务器
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) > 0)
{
fos.write(buffer , 0 , len);
}
return SUCCESS;
}
}
上面的Action与普通的action并没有太大的不同,一样提供了upload和title两个属性,这两个属性分别对应前面的标题和文件域。该Action还包含了两个属性:uploadFileName和uploadContentType这两个属性分别用于封装上传文件的文件名,上传文件的文件类型。因为Action类直接通过File类型属性封装了上传文件的文件内容,但这个File属性无法获取上传文件的文件名和文件类型,所以Struts2直接将文件域包含的上传文件名和文件类型的信息封装到uploadFielName和uploadContentType属性中。可以认为:如果表单中包含一个name属性为xxx的文件域,则对应的Action需要使用3个属性来封装文件域的信息:
类型为File的xxx属性封装了该文件域对应的文件内容。
类型为String的xxxFileName属性封装了该文件域对应的文件的文件名。
类型为String的xxxContextType属性封装了该文件域对应的文件的文件类型。
上面的Action还包含了一个savePath属性,该属性的值通过配置文件来设置,从而允许的动态设置该属性的值。这也是典型的依赖注入。
配置文件上传的Action与配置普通的Action并没有太大的不同,一个小小的区别是该Action还配置了一个<param../>元素,该元素用于为Action的属性动态分配属性值。如:
<param name="savePath">/upload</param>
该<param.../>元素设置了uploadAction的savePath属性值。将上传的文件保存到web项目的upload目录下。
在web.xml中增加ActionContextCleaUp的配置,这个类也是一个Filter,此处加载该Filter的原因是减少struts2的文件上传引用过程中的未知异常。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Struts 2.0 Hello World </display-name>
<filter>
<filter-name>struts2</filter-name>
<filter-class>
org.apache.struts2.dispatcher.FilterDispatcher
</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/* </url-pattern>
</filter-mapping>
<!-- 配置Struts2的CleanUp的Filter -->
<filter>
<filter-name>struts-cleanup</filter-name>
<filter-class>org.apache.struts2.dispatcher.ActionContextCleanUp</filter-class>
</filter>
<!-- 定义Struts2的CleanUp Filter拦截的URL -->
<filter-mapping>
<filter-name>struts-cleanup</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<welcome-file-list>
<welcome-file>index.html </welcome-file>
</welcome-file-list>
</web-app>
上面完成一个简单的上传,一次上传一个文件,以被上文件的文件名保存,遇到同名的文件将覆盖掉。
2手动实现文件过滤
为了让上面的Action增加文件过滤的功能,在上面的Action中增加了如下的方法:
/**
*过滤文件类型
*@param types 系统所有允许上传的文件类型
*@return 如果上传文件的文件类型允许上传,返回null,否则返回input字符串。
*/
public String filterType(String[] types){
//取得上传文件的文件类型
String fileType=getUploadContentType();
//遍历
for(String type:types){
if(type.equals(fileType)){
return null;
}
}
return INPUT;
}
filterType方法判断上传文件的文件类型是否在允许上传文件类型列表中,为了让应用程序可以动态配置允许上传文件列表,可以为该Action类增加一个allowTypes属性,该属性的值列出了所有允许上传的文件类型,并在struts.xml的Action中配置相应的<param.../>。在Action类增加的代码如下:
//定该Action允许上传的文件类型
private String allowTypes;
//allowTypes属性的setter和getter方法
public String getAllowTypes(){
return allowTypes;
}
public void setAllowTypes(String allowTypes){
this.allowTypes=allowTypes;
}
<param name="allowTypes">image/bmp,image/png,image/gif,image/jpg</param>
这里的image/bmp,image/png等分别对应于.bmg,.png类型的文件。最下面我们列出了struts2支持的文件类型。
下面在该Actin的execute方法中增加判断,判断上传文件的文件类型是否允许上传。
@Override
public String execute() throws Exception
{
//将允许上传文件类型的字符串以英文逗号分解成字符串数组
//判断当前文件类型是否允许上传
String filterResult=filterType(getAllowTypes().split(","));
//如果当前文件类型不允许上传,如果类型不匹配直接返回INPUT跳出,否则就继续执行上传操作。
if(filterResult != null){
addFieldError("shangchuan","上传文件的类型不正确!");
return filterResult;
}
//以服务器的文件保存地址和原文件名建立上传文件输出流
FileOutputStream fos = new FileOutputStream(getSavePath() + "\\" + getUploadFileName());
//以上传文件建立一个文件上传流
FileInputStream fis = new FileInputStream(getUpload());
//将上传文件的内容写入服务器
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) > 0)
{
fos.write(buffer , 0 , len);
}
return SUCCESS;
}
}
3拦截器实现文件过滤
struts2还提供了一个文件上传的拦截器,通过配置该拦截器,可以轻松地实现文件过滤,struts2中文件上传的过滤器是fileUpload,为了让该拦截器其作用,只需要在该Action中配置该拦截器引用即可。
配置fileUpload拦截器时,可以为其指定两个参数:
allowTypes:该参数指定允许上传的文件类型,多个文件类型之间以英文逗号(,)隔开。
maximumSize:该参数指定允许上传的文件大小,单位是字节。
通过配置fileUpload的拦截器,可以轻松地实现文件过滤,当文件过来失败后,系统自动转入input逻辑视图,因此必须为Action配置名为input的逻辑视图,除此之外,如果使用文件上传拦截器来过滤文件大小,或过滤文件内容则必须显式地为该Action配置引用struts2默认的拦截器栈:defaultStack。
<!--配置fileUpload的拦截器-->
<interceptor-ref name="fileUpload">
<!--配置允许上传的文件类型-->
<param name="allowedTypes">image/bmgp,image/png,image/gif,image/jpeg,image/x-png</param>
<!--配置允许上传的文件大小-->
<param name="maximumSize">2000</param>
</interceptor-ref>
<interceptor-ref name="defaultStack"/>
当使用拦截器实现文件过滤时,如果上传失败,系统返回原来上传页面,没有任何提示。这种方式不够友好。为了在上传页面输出失败提示,需要在页面中增加<s:fielderror/>输出错误提示,但是这个提示是英文的,对于中的Web应用而言,应该使用国际化信息替换它。上传文件太大的提示信息的key是"struts.message.error.file.too.large"如果在自己的国际化资源文件中增加该key的消息,将改变该提示信息。上传文件类型不匹配的提示信息的key是"struts.messages.error.content.type.not.allowed"
4同时上传多个文件
struts2也可以很方便地实现多文件上传,如果页面中有多个文件域需要实现上传则可以为每个文件域提供3个属性,分别封装该文件域对应的文件名,文件类型和文件内容。使用数组和List类处理同时上传多个文件。
<%@ page language="java" contentType="text/html;charset=UTF-8"%>
<html>
<title>多文件上传</title>
<body>
<form action="kk/upload.action" method="post" enctype="multipart/form-data">
文件标题:<input type="text" name="title"/><br/>
文件1:<input type="file" name="upload"/><br/>
文件2:<input type="file" name="upload"/><br/>
文件3:<input type="file" name="upload"/><br/>
<input value="上传" type="submit"/>
</form>
</body>
</html>
上面的三个文件有相同的name属性,因此只需要使用一个数组属性就可以封装该文件域。当然,实际上需要三个数组分别封装文件名,文件类型,和文件内容。如下:
package action;
import java.io.*;
import com.opensymphony.xwork2.ActionSupport;
import org.apache.struts2.ServletActionContext;
public class uploadAction extends ActionSupport
{
//封装文件标题请求参数的属性
private String title;
//封装上传文件域的属性
private File[] upload;
//封装上传文件类型的属性
private String[] uploadContentType;
//封装上传文件名的属性
private String[] uploadFileName;
//接受依赖注入的属性
private String savePath;
//接受依赖注入的方法
public void setSavePath(String value)
{
this.savePath = value;
}
//返回上传文件的保存位置
private String getSavePath() throws Exception
{
System.out.println("**************"+savePath);
return ServletActionContext.getRequest().getRealPath(savePath);
}
//文件标题的setter和getter方法
public void setTitle(String title)
{
this.title = title;
}
public String getTitle()
{
return (this.title);
}
//上传文件对应文件内容的setter和getter方法
public void setUpload(File[] upload)
{
this.upload = upload;
}
public File[] getUpload()
{
return (this.upload);
}
//上传文件的文件类型的setter和getter方法
public void setUploadContentType(String[] uploadContentType)
{
this.uploadContentType = uploadContentType;
}
public String[] getUploadContentType()
{
return (this.uploadContentType);
}
//上传文件的文件名的setter和getter方法
public void setUploadFileName(String[] uploadFileName)
{
this.uploadFileName = uploadFileName;
}
public String[] getUploadFileName()
{
return (this.uploadFileName);
}
//上传文件函数:
public String uploadfile() throws Exception
{
//取得需要上传的文件数组
File[] files=getUpload();
//遍历每个需要上传的文件
for(int i=0;i<files.length;i++){
//以服务器的文件保存地址和原文件名建立上传文件输出流
FileOutputStream fos = new FileOutputStream(getSavePath() + "\\" + getUploadFileName()[i]);
//以上传文件建立一个文件上传流
FileInputStream fis = new FileInputStream(files[i]);
//将上传文件的内容写入服务器
byte[] buffer = new byte[1024];
int len = 0;
while ((len = fis.read(buffer)) > 0)
{
fos.write(buffer , 0 , len);
}
}
return SUCCESS;
}
}
上面是使用数组完成多个文件上传,也可以使用List来上传,方式基本一样,只是将数组换成List而已。它与单个文件上传并没有复杂多少。
5使用Struts2控制文件下载
可能很多人会觉得,文件下载太简单,直接在页面上给出一个超级链接,该链接的href属性等于要下载文件的文件名,不就可以实现文件下载了吗?大部分时候的确可以实现文件下载,但如果该文件的文件名为中文文件名,则会导致下载失败;或者应用程序需要在让用户下载之前进行进一步检查,比如判断用户是否有足够权限来下载该文件等。
看下面的一个原始的下载页面代码片段:
<h1>原始的下载</h1>
<ul><li>
<!-- 包含中文文件名的下载链接 -->
下载Struts2的Logo:<a href="images/中.gif">下载图形文件</a>
</li><li>
下载Struts2的Logo的压缩文件:<a href="images/struts-gif.zip">下载压缩文件</a>
</li></ul>
上面页面的包含两个下载的超级链接,两个链接的资源都是存在的,但因为第一个资源文件的文件名是中文文件名,如果单击第一个超级链接,将出现如下图所示的页面。
从图中页面中椭圆形框包围的地方,我们看到被下载的文件名变成了包含大量%的字符串,很明显,这种文件名显然无法取得需要下载的文件。
为了解决这个问题,我们使用Struts2的文件下载支持来下载该文件。
实现文件下载的Action
Struts2的文件下载Action与普通的Action并没有太大的不同,仅仅是该Action需要提供一个返回InputStream流的方法,该输入流代表了被下载文件的入口。该Action类的代码如下:
public class FileDownloadAction implements Action
{
//该属性是依赖注入的属性,该属性可以在配置文件中动态指定该属性值
private String inputPath;
//依赖注入该属性值的setter方法
public void setInputPath(String value)
{
inputPath = value;
}
/*
下载用的Action应该返回一个InputStream实例,
该方法对应在result里的inputName属性值为targetFile
*/
public InputStream getTargetFile() throws Exception
{
return ServletActionContext.getServletContext().getResourceAsStream(inputPath);
}
//处理用户请求的execute方法,该方法返回success字符串
public String execute() throws Exception
{
return SUCCESS;
}
}
从上面的Action中看到,该Action中包含了一个getTargetFile()方法,该方法返回一个InputStream输入流,这个输入流返回的是下载目标文件的入口。该方法的方法名为getTargetFile,表明该Action有一个targetFile属性来返回下载文件。
一旦我们定义了该Action,就可通过该Action来实现文件下载。
配置Action
配置该文件下载的Action与配置普通的Action并没有太大的不同,需要在配置普通Action的基础之上,在加上额外的download的拦截器引用。
除此之外,关键是需要配置一个类型为stream的结果,配置stream类型的结果时需要指定如下四个属性:
contentType:指定被下载文件的文件类型。
inputName:指定被下载文件的入口输入流。
contentDisposition:指定下载的文件名。
bufferSize:指定下载文件时的缓冲大小。
因为stream结果类型的逻辑视图是返回给客户端一个输入流,因此无需指定location属性。
提示:配置stream类型的结果时,因为无需指定实际的显示的物理资源,所以无需指定location属性,只需要指定inputName属性,该属性指向被下载文件。
下面是配置该下载所用的Action类的配置文件片段:
<?xml version="1.0" encoding="GBK"?>
<!-- 指定Struts2配置文件的DTD信息 -->
<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<!-- 配置Struts2国际化资源文件的baseName -->
<constant name="struts.custom.i18n.resources" value="globalMessages"/>
<!-- 配置Struts2应用的编码集 -->
<constant name="struts.i18n.encoding" value="GBK"/>
<package name="lee" extends="struts-default">
<!-- 配置下载的拦截器引用 -->
<default-action-ref name="download"/>
<action name="download" class="lee.FileDownloadAction">
<!-- 指定被下载资源的位置 -->
<param name="inputPath">\images\中.gif</param>
<!-- 配置结果类型为stream的结果 -->
<result name="success" type="stream">
<!-- 指定下载文件的文件类型 -->
<param name="contentType">image/gif</param>
<!-- 指定下载文件的文件位置 -->
<param name="inputName">targetFile</param>
<param name="contentDisposition">filename="struts.gif"</param>
<!-- 指定下载文件的缓冲大小 -->
<param name="bufferSize">4096</param>
</result>
</action>
</package>
</struts>
**************************
使用jakarta_Commons_FileUpload
从V1.1版开始,FileUpload就开始支持servlet和porlet的文件上传请求,我们将在servlet环境里实现文件上传。
为了确保是一个文件上传请求,FileUpload是通过调用一个静态的方法来判断:
boolean isMultipart=ServletFileUpload.isMultipartContent(request);
上传项目只要足够小,就应该保留在内存里。
较大的项目应该被写在硬盘的临时文件上。
限制项目在内存中所占的控件,限制最大的上传请求,并设置临时文件的位置。
处理上面的情况很简单:
//创建一个工厂函数:
DiskFileItemFactory factory=new DiskFileItemFactory();
//设置工厂函数常量
factory.setSizeThreshold(yourMaxMemotySize);//上传所用的最大内存大小
factory1.setRepository(yourTempDirectory);//设置临时文件保存路径
//创建一个文件上传处理
ServletFileUpload upload = new ServletFileUpload(factory);
//设置最大上传文件
upload.setSizeMax(1024*1024*20);
//转换request
List<FileItem> items = upload.parseRequest(request);
上面的语句执行完,你会得到一个待处理的文件项目列表。你可以这样来处理文件上传域和正常的表单域:
Iterator iter=items.iterator();
while(iter.hasNext()){
FileItem item=(FileItem)iter.next();
//对于普通的表单域
if(item.isFormField()){
String name=item.getFieldName();//获得普通表单域的名称
String value=item.getString();//获得普通表单域的值
....
}
//对于文件上传域
if(!item.isFormField()){
String fieldName=item.getFieldName();//获得文件域的名称
String fileName=item.getName();//获得文件名
String contentType=item.getContentType();//获得文件类型
long sizeInBytes=item.getSize();//获得上传文件的大小
//以当前时间来生成上传文件的文件名
FileOutputStream fos=new FileOutputStream(request.getRealPath("/")+System.currentTimeMillis()+fileName.substring(fileName.lastIndexOf("."),fileName.length()));
//如果上传文件域对应文件的内容已经在内容中
if(item.isInMemory()){
//用get()方法来获得数据的二进制数据形式
fos.write(item.get());
}
//如果文件内容不完全在内存中
else{
//获取上传文件内容的输入流
InputStream is=item.getInputStream();
byte[] buffer=new byte[1024];
int len;
//读取上传文件的内容,并将其写入服务器的文件中
while((len=is.read(buffer))>0){
fos.write(buffer,0,len);
}
is.close();
fos.close();
}
....
}
}
注意因为使用的enctype="multipart/form-data"提交表单,在后台想获取普通表单元素(如文本域)的内容不能使用reqest.getParameter("name")形式获得,这样获取会报错。应该向上面那样先判断是否是普通文本域,在进行处理。
监听进度:
ProgressListener progessListener=new ProgressListener(){
private long megaBytes=-1;
public void update(long pBytesRead,long pContentLength,int pItems){
long mBytes=pBytesRead/1000000;
if(megaBytes==mBytes){
return;
}
megaBytes=mBytes;
System.out.println("We area currently reading item"+pItems);
if(pContentLength==-1){
System.out.println("So far,"+ pBytesRead+"bytes have been read.");
}else{
System.out.println("So far,"+pBytesRead+"of "+ pContentLength+ "bytes have been read.");
}
}
};
**********************************
注意采用struts2上传框架应该配置struts.multipart.maxSize这个常量的值,如果不配置struts2会给它一个默认值,这样如果上传大一些的文件比如6M,就不会被上传:
<constant name="struts.multipart.maxSize" value="1073741824"/>
*************************************
struts2支持的文件类型:
‘.a‘ : ‘application/octet-stream‘,
‘.ai‘ : ‘application/postscript‘,
‘.aif‘ : ‘audio/x-aiff‘,
‘.aifc‘ : ‘audio/x-aiff‘,
‘.aiff‘ : ‘audio/x-aiff‘,
‘.au‘ : ‘audio/basic‘,
‘.avi‘ : ‘video/x-msvideo‘,
‘.bat‘ : ‘text/plain‘,
‘.bcpio‘ : ‘application/x-bcpio‘,
‘.bin‘ : ‘application/octet-stream‘,
‘.bmp‘ : ‘image/x-ms-bmp‘,
‘.c‘ : ‘text/plain‘,
# Duplicates :(
‘.cdf‘ : ‘application/x-cdf‘,
‘.cdf‘ : ‘application/x-netcdf‘,
‘.cpio‘ : ‘application/x-cpio‘,
‘.csh‘ : ‘application/x-csh‘,
‘.css‘ : ‘text/css‘,
‘.dll‘ : ‘application/octet-stream‘,
‘.doc‘ : ‘application/msword‘,
‘.dot‘ : ‘application/msword‘,
‘.dvi‘ : ‘application/x-dvi‘,
‘.eml‘ : ‘message/rfc822‘,
‘.eps‘ : ‘application/postscript‘,
‘.etx‘ : ‘text/x-setext‘,
‘.exe‘ : ‘application/octet-stream‘,
‘.gif‘ : ‘image/gif‘,
‘.gtar‘ : ‘application/x-gtar‘,
‘.h‘ : ‘text/plain‘,
‘.hdf‘ : ‘application/x-hdf‘,
‘.htm‘ : ‘text/html‘,
‘.html‘ : ‘text/html‘,
‘.ief‘ : ‘image/ief‘,
‘.jpe‘ : ‘image/jpeg‘,
‘.jpeg‘ : ‘image/jpeg‘,
‘.jpg‘ : ‘image/jpeg‘,
‘.js‘ : ‘application/x-javascript‘,
‘.ksh‘ : ‘text/plain‘,
‘.latex‘ : ‘application/x-latex‘,
‘.m1v‘ : ‘video/mpeg‘,
‘.man‘ : ‘application/x-troff-man‘,
‘.me‘ : ‘application/x-troff-me‘,
‘.mht‘ : ‘message/rfc822‘,
‘.mhtml‘ : ‘message/rfc822‘,
‘.mif‘ : ‘application/x-mif‘,
‘.mov‘ : ‘video/quicktime‘,
‘.movie‘ : ‘video/x-sgi-movie‘,
‘.mp2‘ : ‘audio/mpeg‘,
‘.mp3‘ : ‘audio/mpeg‘,
‘.mpa‘ : ‘video/mpeg‘,
‘.mpe‘ : ‘video/mpeg‘,
‘.mpeg‘ : ‘video/mpeg‘,
‘.mpg‘ : ‘video/mpeg‘,
‘.ms‘ : ‘application/x-troff-ms‘,
‘.nc‘ : ‘application/x-netcdf‘,
‘.nws‘ : ‘message/rfc822‘,
‘.o‘ : ‘application/octet-stream‘,
‘.obj‘ : ‘application/octet-stream‘,
‘.oda‘ : ‘application/oda‘,
‘.p12‘ : ‘application/x-pkcs12‘,
‘.p7c‘ : ‘application/pkcs7-mime‘,
‘.pbm‘ : ‘image/x-portable-bitmap‘,
‘.pdf‘ : ‘application/pdf‘,
‘.pfx‘ : ‘application/x-pkcs12‘,
‘.pgm‘ : ‘image/x-portable-graymap‘,
‘.pl‘ : ‘text/plain‘,
‘.png‘ : ‘image/png‘,
‘.pnm‘ : ‘image/x-portable-anymap‘,
‘.pot‘ : ‘application/vnd.ms-powerpoint‘,
‘.ppa‘ : ‘application/vnd.ms-powerpoint‘,
‘.ppm‘ : ‘image/x-portable-pixmap‘,
‘.pps‘ : ‘application/vnd.ms-powerpoint‘,
‘.ppt‘ : ‘application/vnd.ms-powerpoint‘,
‘.ps‘ : ‘application/postscript‘,
‘.pwz‘ : ‘application/vnd.ms-powerpoint‘,
‘.py‘ : ‘text/x-python‘,
‘.pyc‘ : ‘application/x-python-code‘,
‘.pyo‘ : ‘application/x-python-code‘,
‘.qt‘ : ‘video/quicktime‘,
‘.ra‘ : ‘audio/x-pn-realaudio‘,
‘.ram‘ : ‘application/x-pn-realaudio‘,
‘.ras‘ : ‘image/x-cmu-raster‘,
‘.rdf‘ : ‘application/xml‘,
‘.rgb‘ : ‘image/x-rgb‘,
‘.roff‘ : ‘application/x-troff‘,
‘.rtx‘ : ‘text/richtext‘,
‘.sgm‘ : ‘text/x-sgml‘,
‘.sgml‘ : ‘text/x-sgml‘,
‘.sh‘ : ‘application/x-sh‘,
‘.shar‘ : ‘application/x-shar‘,
‘.snd‘ : ‘audio/basic‘,
‘.so‘ : ‘application/octet-stream‘,
‘.src‘ : ‘application/x-wais-source‘,
‘.sv4cpio‘: ‘application/x-sv4cpio‘,
‘.sv4crc‘ : ‘application/x-sv4crc‘,
‘.swf‘ : ‘application/x-shockwave-flash‘,
‘.t‘ : ‘application/x-troff‘,
‘.tar‘ : ‘application/x-tar‘,
‘.tcl‘ : ‘application/x-tcl‘,
‘.tex‘ : ‘application/x-tex‘,
‘.texi‘ : ‘application/x-texinfo‘,
‘.texinfo‘: ‘application/x-texinfo‘,
‘.tif‘ : ‘image/tiff‘,
‘.tiff‘ : ‘image/tiff‘,
‘.tr‘ : ‘application/x-troff‘,
‘.tsv‘ : ‘text/tab-separated-values‘,
‘.txt‘ : ‘text/plain‘,
‘.ustar‘ : ‘application/x-ustar‘,
‘.vcf‘ : ‘text/x-vcard‘,
‘.wav‘ : ‘audio/x-wav‘,
‘.wiz‘ : ‘application/msword‘,
‘.wsdl‘ : ‘application/xml‘,
‘.xbm‘ : ‘image/x-xbitmap‘,
‘.xlb‘ : ‘application/vnd.ms-excel‘,
‘.xls‘ : ‘application/excel‘,
‘.xls‘ : ‘application/vnd.ms-excel‘,
‘.xml‘ : ‘text/xml‘,
‘.xpdl‘ : ‘application/xml‘,
‘.xpm‘ : ‘image/x-xpixmap‘,
‘.xsl‘ : ‘application/xml‘,
‘.xwd‘ : ‘image/x-xwindowdump‘,
‘.zip‘ : ‘application/zip‘,
firefox 和 ie 的文件类型区别
firefox image/jpeg image/bmp image/gif image/png
ie 6 image/pjpeg image/bmp image/gif image/x-png
ie 7 image/pjpeg image/bmp image/gif image/x-png
ie 8 image/pjpeg image/bmp image/gif image/x-png