仓库源文站点原文


layout: post title: "使用Apache的Fileupload工具实现文件上传" categories: Tools tags: 文件上传 Apache Fileupload IO

author: 张乘辉

Maven依赖

<dependency>
    <groupId>commons-fileupload</groupId>
    <artifactId>commons-fileupload</artifactId>
    <version>1.3.2</version>
</dependency>

相关类

上传三大步

  1. 创建工厂:DiskFileItemFactory factory = new DiskFileItemFactory();
  2. 创建解析器:ServletFileUpload sfu = new ServletFileUpload(factory);
  3. 使用解析器解析request:List<FileItem> fileItemList = sfu.parseRequest(request);

FileItem API

boolean isFormField();// 是否为普通表单项!返回true为普通表单项,如果为false即文件表单项!
String getFieldName();// 返回当前表单项的名称;
String getString(String charset);// 返回表单项的值;
String getName();// 返回上传的文件名称
long getSize();// 返回上传文件的字节数
InputStream getInputStream();// 返回上传文件对应的输入流
void write(File destFile);// 把上传的文件内容保存到指定的文件中。
String getContentType();

需要注意的一些上传细节

保存地址

上传到服务器的地址最好是在WEB-INF下,因为这个目录浏览器是访问不到的

文件名称相关问题

String filename = fi2.getName();
    int index = filename.lastIndexOf("\\");
    if(index != -1) {
        filename = filename.substring(index+1);
    }

目录打散

不能在一个目录下存放之多文件:

上传文件的大小限制

缓存大小与临时目录

实战演练

请求表单

<form action="xxx" method="post" enctype="multipart/form-data">
  用户名:<input type="text" name="username"/><br/>
  照 片:<input type="file" name="zhaoPian"/><br/>
  <input type="submit" value="上传"/>
</form>

注:request.getParametere("xxx");这个方法在表单为enctype="multipart/form-data"时,它作废了。它永远都返回null ,ServletInputStream request.getInputStream();包含整个请求的体!

后台处理

@Controller
@RequestMapping("/fileupload")
public class UpLoadController {

       @RequestMapping("/upload")
       public void upload(HttpServletRequest request, HttpServletResponse response)
                     throws ServletException, IOException {
              response.setContentType("text/html;chatset=utf-8");

              /*
               * 上传三大步
               */
              DiskFileItemFactory factory=new DiskFileItemFactory();
              ServletFileUpload sfu=new ServletFileUpload(factory);
              try {
                     List<FileItem> fList = sfu.parseRequest(request);

                     //得到文件表单项
                     FileItem fileItem = fList.get(1);

                     // 得到文件保存根路径
                     String root=getServletContext().getRealPath("/upload/files/");

                     //得到文件名字
                     String fName = fileItem.getName();
                     //处理文件名绝对路径的问题
                     int lastIndexOf = fName.lastIndexOf("\\");
                     if(lastIndexOf!=-1) fName=fName.substring(lastIndexOf+1);
                     //处理相同文件名字的问题
                     String saveName=CommonUtils.uuid()+"_"+fName;

                     //得到hashCode
                     int hCode = fName.hashCode();
                     String hex = Integer.toHexString(hCode);
                     //生成完整的抽象路径名
                     File dirFile = new File(root,hex.charAt(0)+"/"+hex.charAt(1));
                     //生成抽象路径名指定的目录
                     dirFile.mkdirs();

                     //创建完整文件目录
                     File destFile = new File(dirFile,saveName);
                     //输出,保存文件
                     try {
                           fileItem.write(destFile);
                     } catch (Exception e) {
                           throw new RuntimeException(e);
                     }

              } catch (FileUploadException e) {
                     throw new RuntimeException(e);
              }
       }
}

总结

源码大致流程

源码

public abstract class FileUploadBase相关源码

// 创建解析方法
public List<FileItem> parseRequest(HttpServletRequest req)
    throws FileUploadException {
        return parseRequest(new ServletRequestContext(req));
    }
public List<FileItem> parseRequest(RequestContext ctx)
            throws FileUploadException {
        List<FileItem> items = new ArrayList<FileItem>();
        boolean successful = false;
        try {
            FileItemIterator iter = getItemIterator(ctx);
            FileItemFactory fac = getFileItemFactory();//依赖表单工厂
            if (fac == null) {
                throw new NullPointerException("No FileItemFactory has been set.");
            }
            while (iter.hasNext()) {
                final FileItemStream item = iter.next();
                // Don't use getName() here to prevent an InvalidFileNameException.
                final String fileName = ((FileItemIteratorImpl.FileItemStreamImpl) item).name;
                // 使用表单工厂创建表单项
                FileItem fileItem = fac.createItem(item.getFieldName(), item.getContentType(),
                                                   item.isFormField(), fileName);
                items.add(fileItem);
                try {
                     //把数据写在硬盘缓存区
                    Streams.copy(item.openStream(), fileItem.getOutputStream(), true);
                } catch (FileUploadIOException e) {
                    throw (FileUploadException) e.getCause();
                } catch (IOException e) {
                    throw new IOFileUploadException(format("Processing of %s request failed. %s",
                                                           MULTIPART_FORM_DATA, e.getMessage()), e);
                }
                final FileItemHeaders fih = item.getHeaders();
                fileItem.setHeaders(fih);
            }
            successful = true;
            return items;
        } catch (FileUploadIOException e) {
            throw (FileUploadException) e.getCause();
        } catch (IOException e) {
            throw new FileUploadException(e.getMessage(), e);
        } finally {
            if (!successful) {
                for (FileItem fileItem : items) {
                    try {
                        fileItem.delete();
                    } catch (Throwable e) {
                        // ignore it
                    }
                }
            }
        }
    }

注:ServletFileUpload继承FileUploadBase

DiskFileItemFactory源码

// 创建表单项
public FileItem createItem(String fieldName, String contentType,
            boolean isFormField, String fileName) {
        DiskFileItem result = new DiskFileItem(fieldName, contentType,
                isFormField, fileName, sizeThreshold, repository);
        FileCleaningTracker tracker = getFileCleaningTracker();
        if (tracker != null) {
            tracker.track(result.getTempFile(), result);
        }
        return result;
    }

DiskFileItem源码

public void write(File file) throws Exception {
        if (isInMemory()) {
            FileOutputStream fout = null;
            try {
                fout = new FileOutputStream(file);
                fout.write(get());
            } finally {
                if (fout != null) {
                    fout.close();
                }
            }
        } else {
            //获得缓存文件地址
            File outputFile = getStoreLocation(); 
            if (outputFile != null) {
                // Save the length of the file
                size = outputFile.length();
                /*
                 * The uploaded file is being stored on disk
                 * in a temporary location so move it to the
                 * desired file.
                 */
                if (!outputFile.renameTo(file)) {
                    BufferedInputStream in = null;
                    BufferedOutputStream out = null;
                    try {
                         //把缓存数据写到指定文件中
                        in = new BufferedInputStream(
                            new FileInputStream(outputFile));
                        out = new BufferedOutputStream(
                                new FileOutputStream(file));
                        IOUtils.copy(in, out);
                    } finally {
                        if (in != null) {
                            try {
                                in.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                        if (out != null) {
                            try {
                                out.close();
                            } catch (IOException e) {
                                // ignore
                            }
                        }
                    }
                }
            } else {
                /*
                 * For whatever reason we cannot write the
                 * file to disk.
                 */
                throw new FileUploadException(
                    "Cannot write uploaded file to disk!");
            }
        }
    }
 //获得把缓存数据写到硬盘的输出流
 public OutputStream getOutputStream()
        throws IOException {
        if (dfos == null) {
            //获取临时文件地址
            File outputFile = getTempFile(); 
            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
        }
        return dfos;
    }
public InputStream getInputStream()
        throws IOException {
        if (!isInMemory()) {
            return new FileInputStream(dfos.getFile());
        }

        if (cachedContent == null) {
            cachedContent = dfos.getData();
        }
        return new ByteArrayInputStream(cachedContent);
    }
protected File getTempFile() {
        if (tempFile == null) {
            File tempDir = repository;
            if (tempDir == null) {
                tempDir = new File(System.getProperty("java.io.tmpdir"));
            }

            String tempFileName = format("upload_%s_%s.tmp", UID, getUniqueId());

            tempFile = new File(tempDir, tempFileName);
        }
        return tempFile;
    }
public File getStoreLocation() {
        if (dfos == null) {
            return null;
        }
        return dfos.getFile();
    }