上傳模塊在web開發(fā)中是很常見的功能也是很重要的功能懦铺,在web應(yīng)用中需要上傳的可以是圖片、pdf传泊、壓縮包等其它類型的文件雪情,同時(shí)對(duì)于圖片可能需要回顯遵岩,對(duì)于其它文件要能夠支持下載等。在守望博客系統(tǒng)中對(duì)于上傳模塊進(jìn)行統(tǒng)一管理巡通,同時(shí)對(duì)于上傳不同的類型文件尘执,留有自定義實(shí)現(xiàn)機(jī)制的接口,也即可擴(kuò)展扁达。
基于上傳模塊機(jī)制正卧,就可以實(shí)現(xiàn)修改頭像功能了。同時(shí)順帶將修改密碼的功能也一起實(shí)現(xiàn)跪解,這個(gè)修改密碼的功能相對(duì)就很簡(jiǎn)單了。
1签孔、可擴(kuò)展上傳模塊
統(tǒng)一上傳模塊的體現(xiàn)就是上傳所有類型的文件叉讥,都是調(diào)用統(tǒng)一的一個(gè)接口,即上傳接口唯一饥追;同時(shí)對(duì)于具體上傳的類型文件图仓,如有特殊處理的可以自定義實(shí)現(xiàn)處理方法。
對(duì)于上傳的文件能夠有自定義的實(shí)現(xiàn)機(jī)制但绕,則需要一個(gè)上傳文件的處理接口救崔,即:IUploadHandler,內(nèi)容如下:
/**
* 上傳文件處理接口類
*
* @author lzj
* @since 1.0
* @date [2019-07-09]
*/
public interface IUploadHandler {
/**
* 上傳文件處理方法
* 文件上傳成功捏顺,返回文件的相關(guān)信息
* 文件上傳失敗六孵, 返回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;
/**
* 根據(jù)條件列出文件信息
*
* @param distType
* @param userId
* @return
* @throws Exception
*/
public Object list(String distType, String userId) throws Exception;
}
目前本版本中暫定有3個(gè)方法,即:
- upload方法幅骄,用于處理自定義上傳文件方式劫窒;
- download方法,用于處理自定義下載的方式拆座;
- list方法主巍,用于處理自定義列出文件列表的方式。
這里以上傳頭像圖片為例挪凑,則上傳頭像的實(shí)現(xiàn)類UploadAvatarHandler孕索,內(nèi)容如下:
/**
* 上傳頭像處理類
*
* @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();
// 圖片大小不能超過(guò)2M, 2M = 2 * 1024 * 1024B = 2097152B
if (fileSize > 2097152L) {
throw new TipException("您上傳的圖片超過(guò)2M");
}
Config config = configCache.get(Config.CONFIG_IMG_AVATAR_PATH);
// 保存頭像的根目錄
String basePath = config.getConfigValue();
if (!basePath.endsWith("/")) {
basePath += "/";
}
// 根據(jù)當(dāng)前時(shí)間構(gòu)建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個(gè)注意點(diǎn)躏碳,即這個(gè)@Component("_avatar")搞旭,這個(gè)類的名稱最好自定義命名,最好以處理這種文件的類型為名,例如此處的是處理頭像的选脊,所以就是avatar杭抠,但是為了防止重名,所以前綴加上了下劃線恳啥。
另外一個(gè)需要注意的就是偏灿,并不是所有的方法都需要實(shí)現(xiàn),例如此處就沒有實(shí)現(xiàn)download和list方法钝的,因?yàn)轭^像圖片不是通過(guò)流的方式回顯的翁垂,而是直接通過(guò)映射到具體的圖片,同時(shí)也是不需要列出頭像的功能硝桩。
前面說(shuō)過(guò)所有上傳文件沿猜,都是調(diào)用統(tǒng)一的一個(gè)接口,也即是UploadController碗脊,內(nèi)容如下:
/**
* 處理文件上傳下載控制器類
*
* @author lzj
* @date [2019-07-09]
* @since 1.0
*/
@Slf4j
@Controller
public class UploadController {
@Autowired
private ApplicationContext context;
// 用于存儲(chǔ)處理上傳文件對(duì)象
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 {
// 接收參數(shù)
// 獲取上傳文件類型啼肩,參數(shù)名為_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方法,該方法會(huì)將IUploadHandler接口的實(shí)現(xiàn)類都掃描出來(lái)衙伶,同時(shí)以類名為key祈坠,實(shí)例為value返回。
同時(shí)調(diào)用上傳方法時(shí)是需要帶_distType參數(shù)的矢劲,該參數(shù)值要與具體IUploadHandler的實(shí)現(xiàn)類的類名一樣赦拘,例如:上傳頭像就需要將 _distType = _avatar參數(shù)帶過(guò)來(lái)。這樣UploadController就知道具體用哪個(gè)實(shí)現(xiàn)類來(lái)處理芬沉。
2躺同、修改頭像
有了前面的上傳模塊,對(duì)于修改頭像就簡(jiǎn)單多了丸逸,首先需要實(shí)現(xiàn)上傳頭像的實(shí)現(xiàn)類蹋艺,即UploadAvatarHandler類,代碼在上方已經(jīng)羅列了此處省略椭员。
加載出修改頭像頁(yè)面的核心的如下:
/**
* 加載出修改頭像頁(yè)面
*
* @return
*/
@RequestMapping(value = "/user/avatar", method = RequestMethod.GET)
public String avatar(HttpSession session, Model model) {
// session中的信息
User sessionUser = (User) session.getAttribute(Const.SESSION_USER);
// 從數(shù)據(jù)庫(kù)中獲取用戶信息
User user = userService.getById(sessionUser.getUserId());
model.addAttribute("user", user);
return Const.BASE_INDEX_PAGE + "auth/user/avatar";
}
修改頭像车海,運(yùn)用了fullAvatarEditor插件,所以核心的前臺(tái)代碼如下:
<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', //傳遞到上傳接口中的查詢參數(shù)的提交方式隘击。更改該值時(shí)侍芝,請(qǐng)注意更改上傳接口中的查詢參數(shù)的接收方式
src_upload: 0, //是否上傳原圖片的選項(xiàng),有以下值:0-不上傳埋同;1-上傳州叠;2-顯示復(fù)選框由用戶選擇
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("攝像頭已準(zhǔn)備就緒但用戶未允許使用!");
}
else {
alert("攝像頭被占用凶赁!");
}
break;
case 5 :
setTimeout(function () {
window.location.href = window.location.href;
}, 1000);
break;
}
}
);
document.getElementById("upload").onclick = function () {
swf.call("upload");
};
});
</script>
注意:
里面一個(gè)upload_url參數(shù)就是寫上傳接口的咧栗,上述中為:
upload_url: '${rc.contextPath}/upload?_distType=_avatar'
正如在前面討論的一樣的逆甜,需要帶上 _distType參數(shù)
頁(yè)面效果如下:
注意在回顯圖片時(shí),需要加上如下配置:
/**
* 靜態(tài)資源配置
*
* @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);
}
3致板、修改密碼
修改密碼功能相對(duì)簡(jiǎn)單交煞,頁(yè)面效果如下:
此處就只列出修改密碼的核心邏輯,即:
/**
* 修改密碼
*
* @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();
// 接收參數(shù)
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("缺少必要請(qǐng)求參數(shù)");
}
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;
}
關(guān)注我
以你最方便的方式關(guān)注我:
微信公眾號(hào):