序言
现在,绝大部分的应用程序在很多的情况下都需要使用到文件上传与下载的功能,在本文中结合hap利用spirng mvc实现文件的上传和下载,包括上传下载图片、上传下载文档。前端所使用的技术不限,本文重点在于后端代码的实现。希望可以跟随我的一步步实践,最终轻松掌握在hap中的文件上传和下载的具体实现。
案例
1. 数据库设计
表结构
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for tb_fruit
-- ----------------------------
DROP TABLE IF EXISTS `tb_fruit`;
CREATE TABLE `tb_fruit` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`fruitName` varchar(255) DEFAULT NULL,
`picturePath` varchar(255) DEFAULT NULL,
`filePath` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;
字段描述
Id 主键自增
fruitName 水果名称
picturePath 图片路径
filePath 附件2. 先使用代码生成工具根据表结构生成对应的测试
dto层:
2
3 package hbi.core.test.dto;
4
5 /**Auto Generated By Hap Code Generator**/
6
7 import com.hand.hap.mybatis.annotation.ExtensionAttribute;
8
9 import org.hibernate.validator.constraints.Length;
10
11 import javax.persistence.GeneratedValue;
12
13 import javax.persistence.Id;
14
15 import javax.persistence.Table;
16
17 @Table(name = "tb_fruit")
18
19 public class Fruit{
20
21
22
23 public static final String FIELD_ID = "id";
24
25 public static final String FIELD_FRUITNAME = "fruitname";
26
27 public static final String FIELD_PICTUREPATH = "picturepath";
28
29 public static final String FIELD_FILEPATH = "filepath";
30
31
32
33 @Id
34
35 @GeneratedValue
36
37 private Long id;
38
39
40
41 @Length(max = 255)
42
43 private String fruitname;
44
45
46
47 @Length(max = 255)
48
49 private String picturepath;
50
51
52
53 @Length(max = 255)
54
55 private String filepath;
56
57 //省略get/set方法...
58
59 }
60
controller层:
69 package hbi.core.test.controllers;
70
71 import com.hand.hap.attachment.exception.FileReadIOException;
72
73 import com.hand.hap.core.IRequest;
74
75 import com.hand.hap.core.exception.TokenException;
76
77 import com.hand.hap.system.controllers.BaseController;
78
79 import com.hand.hap.system.dto.ResponseData;
80
81 import hbi.core.test.dto.Fruit;
82
83 import hbi.core.test.service.IFruitService;
84
85 import net.sf.json.JSONObject;
86
87 import org.apache.commons.lang.StringUtils;
88
89 import org.springframework.beans.factory.annotation.Autowired;
90
91 import org.springframework.stereotype.Controller;
92
93 import org.springframework.validation.BindingResult;
94
95 import org.springframework.web.bind.annotation.*;
96
97 import org.springframework.web.multipart.MultipartFile;
98
99 import javax.servlet.http.HttpServletRequest;
100
101 import javax.servlet.http.HttpServletResponse;
102
103 import java.io.*;
104
105 import java.net.URLEncoder;
106
107 import java.text.SimpleDateFormat;
108
109 import java.util.*;
110
111
112
113 @Controller
114
115 public class FruitController extends BaseController {
116
117 @Autowired
118
119 private IFruitService service;
120
121 /**
122
123 * word文件路径
124
125 */
126
127 private String wordFilePath="/u01/document/userfile";
128
129 /**
130
131 * 图片文件路径
132
133 */
134
135 private String userPhotoPath = "/u01/document/userphoto";
136
137 /**
138
139 * 文件最大 5M
140
141 */
142
143 public static final Long FILE_MAX_SIZE = 5*1024*1024L;
144
145 /**
146
147 * 允许上传的图片格式
148
149 */
150
151 public static final List<String> IMG_TYPE = Arrays.asList("jpg", "jpeg", "png", "bmp");
152
153 /**
154
155 * 允许上传的文档格式
156
157 */
158
159 public static final List<String> DOC_TYPE = Arrays.asList("pdf", "doc", "docx");
160
161 /**
162
163 * ContentType
164
165 */
166
167 public static final Map<String, String> EXT_MAPS = new HashMap<>();
168
169 static {
170
171 // image
172
173 EXT_MAPS.put("jpg", "image/jpeg");
174
175 EXT_MAPS.put("jpeg", "image/jpeg");
176
177 EXT_MAPS.put("png", "image/png");
178
179 EXT_MAPS.put("bmp", "image/bmp");
180
181 // doc
182
183 EXT_MAPS.put("pdf", "application/pdf");
184
185 EXT_MAPS.put("ppt", "application/vnd.ms-powerpoint");
186
187 EXT_MAPS.put("doc", "application/msword");
188
189 EXT_MAPS.put("doc", "application/wps-office.doc");
190
191 EXT_MAPS.put("docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document");
192
193 }
194
195
196
197 /**
198
199 * @author jiaqing.xu@hand-china.com
200
201 * @date 2017/9/4 17:23
202
203 * @param wordFile photoFile fruitName
204
205 * @return List<Fruit>
206
207 * @description 保存水果信息
208
209 */
210
211 @RequestMapping(value = {"/api/public/upload/fruit/submit"}, method = RequestMethod.POST)
212
213 @ResponseBody
214
215 public List<Fruit> fruitSubmit(@RequestParam(value = "wordFile", required = true) MultipartFile wordFile,@RequestParam(value = "photoFile", required = true) MultipartFile photoFile, @RequestParam(value="fruitName",required = true) String fruitName,HttpServletRequest request){
216
217 IRequest requestContext = createRequestContext(request);
218
219 //上传word文件到磁盘
220
221 Map<String,Object> result1 = uploadFile(wordFile,wordFilePath,DOC_TYPE,null);
222
223 //上传图片文件到磁盘
224
225 Map<String, Object> result2 = uploadFile(photoFile,userPhotoPath, IMG_TYPE, 200);
226
227
228
229 List<Fruit> list = new ArrayList<>();
230
231 //如果创建成功则保存相应路径到数据库
232
233 if((boolean)result1.get("success")&&(boolean)result2.get("success")){
234
235 Fruit fruit = new Fruit();
236
237 fruit.setFruitname(fruitName);
238
239 //设置图片路径
240
241 fruit.setPicturepath((String) result2.get("path"));
242
243 //设置附件路径
244
245 fruit.setFilepath((String) result1.get("path"));
246
247 //插入
249 fruit = service.insertSelective(requestContext,fruit);
251 list.add(fruit);
253 }
255 return list;
256
257 }
258
259
260
261 /**
262
263 * @author jiaqing.xu@hand-china.com
264
265 * @date 2017/9/4 16:18
266
267 * @param
268
269 * @return
270
271 * @description 【通用】 上传文件到磁盘的某一个目录
272
273 */
274
275 public Map<String, Object> uploadFile(MultipartFile file, String path, List<String> fileTypes, Integer ratio){
276
277 Map<String, Object> results = new HashMap<>();
278
279 results.put("success", false);
280
281 if(file == null || file.getSize() < 0 || StringUtils.isBlank(file.getOriginalFilename())){
282
283 results.put("message", "文件为空");
284
285 return results;
286
287 }
288
289 if(file.getSize() > FILE_MAX_SIZE){
290
291 results.put("message", "文件最大不超过" + FILE_MAX_SIZE/1024/1024 + "M");
292
293 return results;
294
295 }
296
297 String originalFilename = file.getOriginalFilename();
298
299 if(!fileTypes.contains(originalFilename.substring(originalFilename.lastIndexOf(".")+1))){
300
301 results.put("message", "文件格式不正确");
302
303 return results;
304
305 }
306
307 SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmssSSS");
308
309 String datetime = formatter.format(new Date());
310
311 String yearmonth = datetime.substring(0, 6);
312
313 // 在基础路径上加上年月路径
314
315 String fileDir = path + "/" + yearmonth;
316
317 // 文件名以 [_时间戳_随机数#原文件名]的形式 随机数防止重复文件名
318
319 String randomNumber = String.valueOf((Math.random() * 90000) + 10000).substring(0, 5);
320
321 String fileName = "_" + datetime + randomNumber + "=" + originalFilename;
322
323 // 文件路径
324
325 String filePath = fileDir + "/" + fileName;
326
329 try {
330
331 // 创建目录
332
333 File dir = new File(fileDir);
334
335 if(!dir.exists() && !dir.isDirectory()){
336
337 dir.mkdirs();
338
339 }
340
341 // 文件输入流
342
343 InputStream is = file.getInputStream();
344
345 // 输出流
346
347 OutputStream os = new FileOutputStream(filePath);
348
349 // 输出文件到磁盘
350
351 int len = 0;
352
353 byte[] buffer = new byte[2048];
354
355 while((len = is.read(buffer, 0, 2048)) != -1){
356
357 os.write(buffer, 0, len);
358
359 }
360
361 os.flush();
362
363 os.close();
364
365 is.close();
366
368
369 //向结果中保存状态消息和存储路径
370
371 results.put("success", true);
372
373 results.put("message", "SUCCESS");
374
375 results.put("path", filePath);
376
377
378
379 // 压缩图片
380
381 if(ratio != null){
382
383 //ImgExif.ExifInfoToRotate(filePath);
384
385 ImgCompress imgCompress = new ImgCompress(filePath);
386
387 imgCompress.setFileName(filePath);
388
389 imgCompress.resizeByWidth(ratio);
390
391 }
392
393 } catch (Exception e) {
394
395 e.printStackTrace();
396
397 results.put("message", "ERROR");
398
399 return results;
400
401 }
402
403
404
405 return results;
406
407 }
408
413 /**
414
415 * @author jiaqing.xu@hand-china.com
416
417 * @date 2017/9/4 17:23
418
419 * @param
420
421 * @return
422
423 * @description 【通用】 读取上传的文件 将文件以流的形式输出
424
425 */
426
427 @RequestMapping(value = "/api/public/read/file")
428
429 public void readFile(@RequestParam String path, HttpServletResponse response) throws FileReadIOException, TokenException {
430
431 Map<String, Object> results = new HashMap<>();
432
433 try {
434
435 File file = new File(path);
436
437 if(!file.exists()){
438
439 results.put("success", false);
440
441 results.put("message", "文件不存在");
442
443 JSONObject jsonObject = JSONObject.fromObject(results);
444
445 response.getWriter().write(jsonObject.toString());
446
447 return;
448
449 }
450
451 // 类型
452
453 String contentType = EXT_MAPS.get(path.substring(path.lastIndexOf(".") + 1));
454
455 // 设置头
456
457 response.addHeader("Content-Disposition", "attachment;filename=\"" + URLEncoder.encode(path.substring(path.indexOf("=") + 1), "UTF-8") + "\"");
458
459 response.setContentType(contentType + ";charset=UTF-8");
460
461 response.setHeader("Accept-Ranges", "bytes");
462
463 // 输出文件
464
465 InputStream is = new FileInputStream(file);
466
467 OutputStream os = response.getOutputStream();
468
469 int len = 0;
470
471 byte[] buffer = new byte[2048];
472
473 while ((len = is.read(buffer, 0, 2048)) != -1){
474
475 os.write(buffer, 0, len);
476
477 }
478
479 os.flush();
480
481 os.close();
482
483 is.close();
484
485 } catch (IOException e) {
487 e.printStackTrace();
489 }
491 }
493 }
说明:
(1)文件保存路径wordFilePath、 水果照片保存路径userPhotoPath可以通过配置文件进行配置。
(2)其中保存的文件的路径在项目的根路径下,比如我的项目在D盘的某一个文件夹下,则生成的word文件的位置为D盘/u01/document/userfile路径下.
3. postman测试
注意点:在使用postman测试时,使用post请求,参数中body为form-data,依次将需要的参数填写到对应的位置。
返回后的数据为插入数据库中的记录。
4. 运行结果
数据库中插入的新的记录:
磁盘中的word文件:
磁盘中的图片:
浏览器进行下载测试: