上傳模塊在web開發中是很常見的功能也是很重要的功能,在web應用中須要上傳的能夠是圖片、pdf、壓縮包等其它類型的文件,同時對於圖片可能須要回顯,對於其它文件要可以支持下載等。在守望博客系統中對於上傳模塊進行統一管理,同時對於上傳不一樣的類型文件,留有自定義實現機制的接口,也便可擴展。javascript
基於上傳模塊機制,就能夠實現修改頭像功能了。同時順帶將修改密碼的功能也一塊兒實現,這個修改密碼的功能相對就很簡單了。java
統一上傳模塊的體現就是上傳全部類型的文件,都是調用統一的一個接口,即上傳接口惟一;同時對於具體上傳的類型文件,若有特殊處理的能夠自定義實現處理方法。web
對於上傳的文件可以有自定義的實現機制,則須要一個上傳文件的處理接口,即:IUploadHandler,內容以下:數據庫
/** * 上傳文件處理接口類 * * @author lzj * @since 1.0 * @date [2019-07-09] */ public interface IUploadHandler { /** * 上傳文件處理方法 * 文件上傳成功,返回文件的相關信息 * 文件上傳失敗, 返回null * * @param file * @param distType * @param userId * @return * @throws Exception */ public Object upload(MultipartFile file, String distType, String userId) throws Exception; /** * 下載文件 * * @param fileId * @param response * @throws Exception */ public void download(String fileId, HttpServletResponse response) throws Exception; /** * 根據條件列出文件信息 * * @param distType * @param userId * @return * @throws Exception */ public Object list(String distType, String userId) throws Exception; }
目前本版本中暫定有3個方法,即:express
這裏以上傳頭像圖片爲例,則上傳頭像的實現類UploadAvatarHandler,內容以下:微信
/** * 上傳頭像處理類 * * @author lzj * @since 1.0 * @date [2019-07-09] */ @Slf4j @Component("_avatar") public class UploadAvatarHandler implements IUploadHandler { @Autowired private IUserService userService; @Resource(name = "configCache") private ICache<Config> configCache; @Override public Object upload(MultipartFile file, String distType, String userId) throws Exception { Map<String, Object> result = new HashMap<String, Object>(); try { // 獲取圖片的大小 long fileSize = file.getSize(); // 圖片大小不能超過2M, 2M = 2 * 1024 * 1024B = 2097152B if (fileSize > 2097152L) { throw new TipException("您上傳的圖片超過2M"); } Config config = configCache.get(Config.CONFIG_IMG_AVATAR_PATH); // 保存頭像的根目錄 String basePath = config.getConfigValue(); if (!basePath.endsWith("/")) { basePath += "/"; } // 根據當前時間構建yyyyMM的文件夾,創建到月的文件夾 String dateDirName = DateUtil.date2Str(new Date(), DateUtil.YEAR_MONTH_FORMAT); basePath += dateDirName; File imageDir = new File(basePath); if (!imageDir.exists()) { imageDir.mkdirs(); } String fileNewName = IdGenarator.guid() + ".jpg"; FileUtil.copy(file.getInputStream(), new FileOutputStream(new File(imageDir, fileNewName))); // 獲取用戶信息 User user = userService.getById(userId); user.setPicture(dateDirName + "/" + fileNewName); // 更新信息 userService.updateById(user); result.put("success", true); result.put("msg", "上傳頭像成功"); } catch (TipException e) { result.put("success", false); result.put("msg", e.getMessage()); } catch (Exception e) { log.error("上傳頭像失敗", e); result.put("success", false); result.put("msg", "上傳頭像失敗"); } return result; } @Override public void download(String fileId, HttpServletResponse response) throws Exception { } @Override public Object list(String distType, String userId) throws Exception { return null; }
這裏有2個注意點,即這個@Component("_avatar"),這個類的名稱最好自定義命名,最好以處理這種文件的類型爲名,例如此處的是處理頭像的,因此就是avatar,可是爲了防止重名,因此前綴加上了下劃線。session
另一個須要注意的就是,並非全部的方法都須要實現,例如此處就沒有實現download和list方法,由於頭像圖片不是經過流的方式回顯的,而是直接經過映射到具體的圖片,同時也是不須要列出頭像的功能。app
前面說過全部上傳文件,都是調用統一的一個接口,也便是UploadController,內容以下:ide
/** * 處理文件上傳下載控制器類 * * @author lzj * @date [2019-07-09] * @since 1.0 */ @Slf4j @Controller public class UploadController { @Autowired private ApplicationContext context; // 用於存儲處理上傳文件對象 private Map<String, IUploadHandler> uploadHandlers; /** * 初始化操做 * * @throws Exception */ @PostConstruct public void init() throws Exception { uploadHandlers = context.getBeansOfType(IUploadHandler.class); } /** * 上傳文件 * * @param file * @param request * @param session * @return */ @RequestMapping(value = "/upload", method = RequestMethod.POST) @ResponseBody public Object upload(@RequestParam(value = "_uploadFile", required = false) MultipartFile file, HttpServletRequest request, HttpSession session) { Object result = null; try { // 接收參數 // 獲取上傳文件類型,參數名爲_fileType String _distType = request.getParameter("_distType"); // 獲取用戶信息 User user = (User) session.getAttribute(Const.SESSION_USER); result = uploadHandlers.get(_distType).upload(file, _distType, user.getUserId()); } catch (Exception e) { log.error("上傳文件失敗", e); } return result; } }
這裏須要注意init方法,該方法會將IUploadHandler接口的實現類都掃描出來,同時以類名爲key,實例爲value返回。post
同時調用上傳方法時是須要帶_distType參數的,該參數值要與具體IUploadHandler的實現類的類名同樣,例如:上傳頭像就須要將 _distType = _avatar參數帶過來。這樣UploadController就知道具體用哪一個實現類來處理。
有了前面的上傳模塊,對於修改頭像就簡單多了,首先須要實現上傳頭像的實現類,即UploadAvatarHandler類,代碼在上方已經羅列了此處省略。
加載出修改頭像頁面的核心的以下:
/** * 加載出修改頭像頁面 * * @return */ @RequestMapping(value = "/user/avatar", method = RequestMethod.GET) public String avatar(HttpSession session, Model model) { // session中的信息 User sessionUser = (User) session.getAttribute(Const.SESSION_USER); // 從數據庫中獲取用戶信息 User user = userService.getById(sessionUser.getUserId()); model.addAttribute("user", user); return Const.BASE_INDEX_PAGE + "auth/user/avatar"; }
修改頭像,運用了fullAvatarEditor插件,因此核心的前臺代碼以下:
<script type="text/javascript"> swfobject.addDomLoadEvent(function () { var swf = new fullAvatarEditor("${rc.contextPath}/static/plugins/fullAvatarEditor/fullAvatarEditor.swf", "${rc.contextPath}/resources/plugins/fullAvatarEditor/expressInstall.swf", "swfContainer", { id: 'swf', upload_url: '${rc.contextPath}/upload?_distType=_avatar', //上傳接口 method: 'post', //傳遞到上傳接口中的查詢參數的提交方式。更改該值時,請注意更改上傳接口中的查詢參數的接收方式 src_upload: 0, //是否上傳原圖片的選項,有如下值:0-不上傳;1-上傳;2-顯示覆選框由用戶選擇 avatar_box_border_width: 0, avatar_sizes: '150*150', avatar_sizes_desc: '150*150像素', avatar_field_names: '_uploadFile' }, function (msg) { console.log(msg); switch (msg.code) { case 1 : break; case 2 : document.getElementById("upload").style.display = "inline"; break; case 3 : if (msg.type == 0) { } else if (msg.type == 1) { alert("攝像頭已準備就緒但用戶未容許使用!"); } else { alert("攝像頭被佔用!"); } break; case 5 : setTimeout(function () { window.location.href = window.location.href; }, 1000); break; } } ); document.getElementById("upload").onclick = function () { swf.call("upload"); }; }); </script>
注意:
裏面一個upload_url參數就是寫上傳接口的,上述中爲:
upload_url: '${rc.contextPath}/upload?_distType=_avatar'
正如在前面討論的同樣的,須要帶上 _distType參數
頁面效果以下:
注意在回顯圖片時,須要加上以下配置:
/** * 靜態資源配置 * * @param registry */ @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { // 映射頭像圖片 String avatarPath = configCache.get(Config.CONFIG_IMG_AVATAR_PATH).getConfigValue(); if (!avatarPath.endsWith("/")) { avatarPath += "/"; } registry.addResourceHandler("/img/avatar/**").addResourceLocations("file:" + avatarPath); }
修改密碼功能相對簡單,頁面效果以下:
此處就只列出修改密碼的核心邏輯,即:
/** * 修改密碼 * * @param request * @param session * @param model * @return */ @RequestMapping(value = "/user/password", method = RequestMethod.POST) @ResponseBody public Result password(HttpServletRequest request, HttpSession session) { Result result = new Result(); try { // 獲取登陸信息 User tempUser = (User) session.getAttribute(Const.SESSION_USER); String userId = tempUser.getUserId(); // 接收參數 String password = request.getParameter("password"); String newPwd = request.getParameter("newPwd"); String confirmNewPwd = request.getParameter("confirmNewPwd"); if (StringUtils.isEmpty(password) || StringUtils.isEmpty(newPwd) || StringUtils.isEmpty(confirmNewPwd)) { throw new TipException("缺乏必要請求參數"); } if (!newPwd.equals(confirmNewPwd)) { throw new TipException("兩次輸入的新密碼不相等"); } // 獲取用戶信息 User user = userService.getById(userId); if (!user.getPassword().equals(StringUtil.md5(password))) { throw new TipException("舊密碼輸入不正確"); } // 修改密碼 user.setPassword(StringUtil.md5(newPwd)); boolean flag = userService.updateById(user); if (!flag) { throw new TipException("修改密碼失敗"); } result.setCode(Result.CODE_SUCCESS); result.setMsg("修改爲功"); } catch (TipException e) { result.setCode(Result.CODE_EXCEPTION); result.setMsg(e.getMessage()); } catch (Exception e) { log.error("修改密碼失敗", e); result.setCode(Result.CODE_EXCEPTION); result.setMsg("修改密碼失敗"); } return result; }
以你最方便的方式關注我:
微信公衆號: