基于SpringBoot從零構(gòu)建博客網(wǎng)站 - 設(shè)計(jì)可擴(kuò)展上傳模塊和開發(fā)修改頭像密碼功能

上傳模塊在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è)面效果如下:


001.jpg

注意在回顯圖片時(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è)面效果如下:


002.png

此處就只列出修改密碼的核心邏輯,即:

/**
 * 修改密碼
 *
 * @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):


架構(gòu)與我
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末斟或,一起剝皮案震驚了整個(gè)濱河市素征,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌萝挤,老刑警劉巖御毅,帶你破解...
    沈念sama閱讀 221,695評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異怜珍,居然都是意外死亡端蛆,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門酥泛,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)今豆,“玉大人,你說(shuō)我怎么就攤上這事揭璃⊥碓洌” “怎么了?”我有些...
    開封第一講書人閱讀 168,130評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵瘦馍,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我应役,道長(zhǎng)情组,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評(píng)論 1 297
  • 正文 為了忘掉前任箩祥,我火速辦了婚禮院崇,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘袍祖。我一直安慰自己底瓣,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,655評(píng)論 6 397
  • 文/花漫 我一把揭開白布蕉陋。 她就那樣靜靜地躺著捐凭,像睡著了一般。 火紅的嫁衣襯著肌膚如雪凳鬓。 梳的紋絲不亂的頭發(fā)上茁肠,一...
    開封第一講書人閱讀 52,268評(píng)論 1 309
  • 那天,我揣著相機(jī)與錄音缩举,去河邊找鬼垦梆。 笑死匹颤,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的托猩。 我是一名探鬼主播印蓖,決...
    沈念sama閱讀 40,835評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼京腥!你這毒婦竟也來(lái)了赦肃?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤绞旅,失蹤者是張志新(化名)和其女友劉穎摆尝,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體因悲,經(jīng)...
    沈念sama閱讀 46,286評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡堕汞,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,375評(píng)論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了晃琳。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片讯检。...
    茶點(diǎn)故事閱讀 40,505評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖卫旱,靈堂內(nèi)的尸體忽然破棺而出人灼,到底是詐尸還是另有隱情,我是刑警寧澤顾翼,帶...
    沈念sama閱讀 36,185評(píng)論 5 350
  • 正文 年R本政府宣布投放,位于F島的核電站,受9級(jí)特大地震影響适贸,放射性物質(zhì)發(fā)生泄漏灸芳。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,873評(píng)論 3 333
  • 文/蒙蒙 一拜姿、第九天 我趴在偏房一處隱蔽的房頂上張望烙样。 院中可真熱鬧,春花似錦蕊肥、人聲如沸谒获。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)批狱。三九已至,卻和暖如春儒洛,著一層夾襖步出監(jiān)牢的瞬間精耐,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工琅锻, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留卦停,地道東北人向胡。 一個(gè)月前我還...
    沈念sama閱讀 48,921評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像惊完,于是被迫代替她去往敵國(guó)和親僵芹。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,515評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容

  • Swift1> Swift和OC的區(qū)別1.1> Swift沒有地址/指針的概念1.2> 泛型1.3> 類型嚴(yán)謹(jǐn) 對(duì)...
    cosWriter閱讀 11,111評(píng)論 1 32
  • 國(guó)家電網(wǎng)公司企業(yè)標(biāo)準(zhǔn)(Q/GDW)- 面向?qū)ο蟮挠秒娦畔?shù)據(jù)交換協(xié)議 - 報(bào)批稿:20170802 前言: 排版 ...
    庭說(shuō)閱讀 11,005評(píng)論 6 13
  • 點(diǎn)擊查看原文 Web SDK 開發(fā)手冊(cè) SDK 概述 網(wǎng)易云信 SDK 為 Web 應(yīng)用提供一個(gè)完善的 IM 系統(tǒng)...
    layjoy閱讀 13,785評(píng)論 0 15
  • 朋友圈看到去年此時(shí)和老同學(xué)的聚會(huì)照片,發(fā)現(xiàn)對(duì)比差別很大凿跳,她近一年來(lái)堅(jiān)持運(yùn)動(dòng)和鍛煉件豌,把她去年說(shuō)的畢業(yè)之后一年漲一斤...
    AK47_10年堅(jiān)持閱讀 214評(píng)論 0 0
  • (一) 圣誕節(jié)一周前的傍晚,在A城靠近城市中軸線的一個(gè)臨街體彩店里控嗜,人聲喧嚷茧彤,五六個(gè)彩民湊在一起,以一個(gè)拿手機(jī)播放...
    普洛斯閱讀 328評(píng)論 0 0