# 文件上传

# 上传概述

文件上传在项目开发过程中是一个使用频率很高的功能,如单图上传多图上传其他附件上传 等等,传统的项目开发中,基本都是遇到一个上传功能就写一个文件上传的方法,如此一来增加了工作量的同时,代码的复用率太低,后期的维护成本也很高,因此我们特地针对各种上传文件场景做了上传文件封装,图片上传文件上传 两种常规的上传方法;框架中我们封装了文件上传的工具类 UploadUtils 以便更友好的给上传文件进行调用,如下所示:

package com.javaweb.common.utils;


import com.javaweb.common.config.UploadFileConfig;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 上传文件工具类
 */
@Component
public class UploadUtils {
    // 表单字段常量
    public static final String FORM_FIELDS = "form_fields";
    // 文件域常量
    public static final String FILE_FIELDS = "file";
    // 定义允许上传的文件扩展名
    private Map<String, String> extMap = new HashMap<String, String>();
    // 文件保存目录路径
    private String uploadPath = UploadFileConfig.uploadFolder;
    // 文件的目录名
    private String dirName = "images";
    // 上传临时路径
    private static final String TEMP_PATH = "temp";
    // 临时存相对路径
    private String tempPath = uploadPath + TEMP_PATH;
    // 单个文件最大上传大小(10M)
    private long fileMaxSize = 1024 * 1024 * 10;
    // 最大文件大小(100M)
    private long maxSize = 1024 * 1024 * 100;
    // 文件保存目录url
    private String saveUrl;
    // 文件最终的url包括文件名
    private List<String> fileUrlList = new ArrayList<>();
    // 上传文件原名
    private List<String> fileNameList = new ArrayList<>();

    /**
     * 构造函数
     */
    public UploadUtils() {
        // 其中images,flashs,medias,files,对应文件夹名称,对应dirName
        // key文件夹名称
        // value该文件夹内可以上传文件的后缀名
        extMap.put("images", "gif,jpg,jpeg,png,bmp");
        extMap.put("flashs", "swf,flv");
        extMap.put("medias", "swf,flv,mp3,wav,wma,wmv,mid,avi,mpg,asf,rm,rmvb");
        extMap.put("files", "doc,docx,xls,xlsx,ppt,htm,html,txt,zip,rar,gz,bz2,mp3,mp4,mp4,mov");
    }

    /**
     * 文件上传
     *
     * @param request
     * @return
     */
    @SuppressWarnings("unchecked")
    public Map<String, Object> uploadFile(HttpServletRequest request, String name) {
        // 验证文件并返回错误信息
        String error = this.validateFields(request, name);
        // 初始化表单元素
        Map<String, Object> fieldsMap = new HashMap<String, Object>();
        if (error.equals("")) {
            fieldsMap = this.initFields(request);
        }
        List<FileItem> fiList = (List<FileItem>) fieldsMap.get(UploadUtils.FILE_FIELDS);
        if (fiList != null) {
            for (FileItem item : fiList) {
                // 上传文件并返回错误信息
                error = this.saveFile(item);
            }
        }
        // 返回结果
        Map<String, Object> result = new HashMap<>();
        result.put("error", error);
        result.put("image", this.fileUrlList);
        result.put("name", this.fileNameList);
        return result;
    }

    /**
     * 上传验证并初始化目录
     *
     * @param request
     * @return
     */
    private String validateFields(HttpServletRequest request, String name) {
        String errorInfo = "";
        // 获取内容类型
        String contentType = request.getContentType();
        int contentLength = request.getContentLength();
        // 初始化上传路径,不存在则创建
        File uploadDir = new File(uploadPath);
        // 目录不存在则创建
        if (!uploadDir.exists()) {
            uploadDir.mkdirs();
        }
        if (contentType == null || !contentType.startsWith("multipart")) {
            // TODO
            System.out.println("请求不包含multipart/form-data流");
            errorInfo = "请求不包含multipart/form-data流";
        } else if (maxSize < contentLength) {
            // TODO
            System.out.println("上传文件大小超出文件最大大小");
            errorInfo = "上传文件大小超出文件最大大小[" + maxSize + "]";
        } else if (!ServletFileUpload.isMultipartContent(request)) {
            // TODO
            errorInfo = "请选择文件";
        } else if (!uploadDir.isDirectory()) {
            // TODO
            errorInfo = "上传目录[" + uploadPath + "]不存在";
        } else if (!uploadDir.canWrite()) {
            // TODO
            errorInfo = "上传目录[" + uploadPath + "]没有写权限";
        } else if (!extMap.containsKey(dirName)) {
            // TODO
            errorInfo = "目录名不正确";
        } else {
            // 上传路径
            uploadPath = UploadFileConfig.uploadFolder + dirName + "/" + name + "/";
            // 保存目录Url
            saveUrl = "/" + dirName + "/" + name + "/";

            // 创建一级目录
            File saveDirFile = new File(uploadPath);
            if (!saveDirFile.exists()) {
                saveDirFile.mkdirs();
            }

            // 创建二级目录(格式:年月日)
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMdd");
            String ymd = sdf.format(new Date());
            uploadPath += ymd + "/";
            saveUrl += ymd + "/";
            File dirFile = new File(uploadPath);
            if (!dirFile.exists()) {
                dirFile.mkdirs();
            }

            // 创建上传临时目录
            File file = new File(tempPath);
            if (!file.exists()) {
                file.mkdirs();
            }
        }
        return errorInfo;
    }

    /**
     * 处理上传内容
     *
     * @return
     */
//	@SuppressWarnings("unchecked")
    private Map<String, Object> initFields(HttpServletRequest request) {
        // 存储表单字段和非表单字段
        Map<String, Object> map = new HashMap<String, Object>();
        // 第一步:判断request
        boolean isMultipart = ServletFileUpload.isMultipartContent(request);
        // 第二步:解析request
        if (isMultipart) {
            // 设置环境:创建一个DiskFileItemFactory工厂
            DiskFileItemFactory factory = new DiskFileItemFactory();
            // 阀值,超过这个值才会写到临时目录,否则在内存中
            factory.setSizeThreshold(1024 * 1024 * 10);
            // 设置上传文件的临时目录
            factory.setRepository(new File(tempPath));
            // 核心操作类:创建一个文件上传解析器。
            ServletFileUpload upload = new ServletFileUpload(factory);
            // 设置文件名称编码(解决上传"文件名"的中文乱码)
            upload.setHeaderEncoding("UTF-8");
            // 限制单个文件上传大小
            upload.setFileSizeMax(fileMaxSize);
            // 限制总上传文件大小
            upload.setSizeMax(maxSize);
            // 使用ServletFileUpload解析器解析上传数据,解析结果返回的是一个List<FileItem>集合,每一个FileItem对应一个Form表单的输入项
            List<FileItem> items = null;
            try {
                items = upload.parseRequest(request);
            } catch (FileUploadException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            // 第3步:处理uploaded items
            if (items != null && items.size() > 0) {
                Iterator<FileItem> iter = items.iterator();
                // 文件域对象
                List<FileItem> list = new ArrayList<FileItem>();
                // 表单字段
                Map<String, String> fields = new HashMap<String, String>();
                while (iter.hasNext()) {
                    FileItem item = iter.next();
                    // 处理所有表单元素和文件域表单元素
                    if (item.isFormField()) {
                        // 如果fileitem中封装的是普通输入项的数据(输出名、值)
                        String name = item.getFieldName();// 普通输入项数据的名
                        String value = item.getString();
                        fields.put(name, value);
                    } else {
                        //如果fileitem中封装的是上传文件,得到上传的文件名称
                        // 文件域表单元素
                        list.add(item);
                    }
                }
                map.put(FORM_FIELDS, fields);
                map.put(FILE_FIELDS, list);
            }
        }
        return map;
    }

    /**
     * 保存文件
     *
     * @param item
     * @return
     */
    private String saveFile(FileItem item) {
        String error = "";
        String fileName = item.getName();
        String fileExt = fileName.substring(fileName.lastIndexOf(".") + 1).toLowerCase();

        if (item.getSize() > maxSize) { // 检查文件大小
            // TODO
            error = "上传文件大小超过限制";
        } else if (!Arrays.<String>asList(extMap.get(dirName).split(",")).contains(fileExt)) {// 检查扩展名
            error = "上传文件扩展名是不允许的扩展名。\n只允许" + extMap.get(dirName) + "格式。";
        } else {
            // 存储文件重命名
            SimpleDateFormat df = new SimpleDateFormat("yyyyMMddHHmmss");
            String newFileName = df.format(new Date()) + new Random().nextInt(1000) + "." + fileExt;

            // 新增文件原名数组(带后缀)
            fileNameList.add(fileName);
            // 新增值文件数组
            String filePath = saveUrl + newFileName;
            fileUrlList.add(filePath);

            // 写入文件
            try {
                File uploadedFile = new File(uploadPath, newFileName);
                item.write(uploadedFile);
            } catch (IOException e) {
                e.printStackTrace();
                System.out.println("上传失败了!!!");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return error;
    }

    /**
     * *********************get/set方法*********************************
     */
    public String getSaveUrl() {
        return saveUrl;
    }

    public String getUploadPath() {
        return uploadPath;
    }

    public long getMaxSize() {
        return maxSize;
    }

    public void setMaxSize(long maxSize) {
        this.maxSize = maxSize;
    }

    public Map<String, String> getExtMap() {
        return extMap;
    }

    public void setExtMap(Map<String, String> extMap) {
        this.extMap = extMap;
    }

    public String getDirName() {
        return dirName;
    }

    public void setDirName(String dirName) {
        this.dirName = dirName;
    }

    public String getTempPath() {
        return tempPath;
    }

    public void setTempPath(String tempPath) {
        this.tempPath = tempPath;
    }

    public List getfileUrlList() {
        return fileUrlList;
    }

    public void setfileUrlList(List fileUrlList) {
        this.fileUrlList = fileUrlList;
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314

# 图片上传

图片上传是使用频率最高的功能,如用户头像上传、会员头像上传、商品图片上传等等,因为框架做了统一方法封装,具体方法如下:

  • 控制器方法
@Autowired
private IUploadService uploadService;

/**
 * 上传图片
 *
 * @param request 网络请求
 * @param name    目录名
 * @return
 */
@PostMapping("/uploadImage/{name}")
public JsonResult uploadImage(HttpServletRequest request, @PathVariable("name") String name) {
    return uploadService.uploadImage(request, name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 接口方法
/**
 * 上传图片
 *
 * @param request 网络请求
 * @param name    目录名
 * @return
 */
JsonResult uploadImage(HttpServletRequest request, String name);
1
2
3
4
5
6
7
8
  • 接口实现方法
/**
 * 上传图片
 *
 * @param request 网络请求
 * @param name    目录名
 * @return
 */
@Override
public JsonResult uploadImage(HttpServletRequest request, String name) {
    UploadUtils uploadUtils = new UploadUtils();
    Map<String, Object> result = uploadUtils.uploadFile(request, name);
    List<String> imageList = (List<String>) result.get("image");
    String imageUrl = CommonUtils.getImageURL(imageList.get(imageList.size() - 1));
    return JsonResult.success(imageUrl, "上传成功");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

# 文件上传

项目开发过程中,除了上传图片比较高频使用外,上传其他附件也是一个比较常用的功能,比如 导入Excel数据上传业务附件 等等,因为框架同时也封装了上传文件的公共方法给业务调用,如下所示:

  • 控制器方法
@Autowired
private IUploadService uploadService;

/**
 * 上传文件(非图片)
 *
 * @param request 网络请求
 * @param name 目录名
 * @return
 */
@PostMapping("/uploadFile")
public JsonResult uploadFile(HttpServletRequest request, @PathVariable("name") String name) {
    return uploadService.uploadFile(request, name);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
  • 接口方法
/**
 * 上传文件
 *
 * @param request 网络请求
 * @param name    目录名
 * @return
 */
JsonResult uploadFile(HttpServletRequest request, String name);
1
2
3
4
5
6
7
8
  • 接口实现方法
@Autowired
private UploadUtils uploadUtils;

/**
 * 上传文件
 *
 * @param request 网络请求
 * @param name    目录名
 * @return
 */
@Override
public JsonResult uploadFile(HttpServletRequest request, String name) {
    uploadUtils.setDirName("files");
    Map<String, Object> result = uploadUtils.uploadFile(request, name);
    List<String> nameList = (List<String>) result.get("name");
    List<String> imageList = (List<String>) result.get("image");
    String imageUrl = CommonUtils.getImageURL(imageList.get(imageList.size() - 1));
    Map<String, Object> map = new HashMap<>();
    map.put("fileName", nameList.get(nameList.size() - 1));
    map.put("fileUrl", imageUrl);
    return JsonResult.success(map, "上传成功");
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22