1. 程式人生 > >JavaWeb開發中重新認識檔案上傳

JavaWeb開發中重新認識檔案上傳

        檔案上傳功能在很多軟體中都是必備的功能之一,所以,檔案上傳也就不顯得那麼有技術含量了,但是如果要把這個功能做好甚至完美,可不是三兩下就能搞定的,其中包含的諸多細節盡在不言中。我們來看一下,做一個上傳功能需要考慮什麼,以apk檔案上傳為例:

       1、上傳的檔案的型別(選擇檔案時對檔案格式做限制)

       2、上傳的檔案的大小(設定檔案上傳的最大範圍)

       3、上傳的檔案的名稱(這裡包括加密後的名稱和realName)

       4、刪除檔案(必須考慮是否能真正刪除)

       5、修改時能否回顯檔案

       6、修改後儲存檔案應該怎麼對檔案進行處理

       7、單獨複製檔案絕對路徑能否下載

       8、檔案的相對路徑生成

       雖然總結出了八條需要考慮的點,但應該還是存在欠缺的地方的。常用的上傳方式有兩種:非同步上傳和同步上傳,現在好像非同步上傳用的比較多一些,下面先以同步上傳方式來說說吧:

       同步上傳就是先僅僅選擇一個檔案,填完其它內容後再統一提交到後臺,在後臺做檔案上傳的邏輯處理並儲存一個完整內容。一般來說,我們在資料庫中都會用一個欄位(URL)來儲存檔案的相對路徑,但是這個最終儲存到資料庫裡的URL是要在後臺生成的,因此,提交表單時將檔案同請求一起傳送到後臺即可。下面以app版本管理功能為例講解。

       app版本新增頁面:


JSP程式碼:

<form:form id="inputForm" modelAttribute="safeFireAppVersion" action="${ctx}/safe/fire/safeFireAppVersion/save" method="post" class="form-horizontal"
		enctype="multipart/form-data">
		<form:hidden path="id"/>
		<sys:message content="${message}"/>		
		<div class="control-group">
			<label class="control-label">版本號:</label>
			<div class="controls">
				<form:input path="code" htmlEscape="false" maxlength="16" class="input-xlarge required"/>
				<span class="help-inline"><font color="red">*</font> </span>
			</div>
		</div>
		<div class="control-group">
			<label class="control-label">版本數:</label>
			<div class="controls">
				<form:input path="codenum" htmlEscape="false" maxlength="3" class="input-xlarge required"
					onkeyup="this.value=this.value.replace(/\D/g,'')"
					onafterpaste="this.value=this.value.replace(/\D/g,'')" />
				<span class="help-inline"><font color="red">*</font> </span>
			</div>
		</div>
		<div class="control-group">
			<label class="control-label">檔案:</label>
			<div class="controls">
				<form:input path="" type="file" name="apkFile" accept=".apk"/>
				<br/>${safeFireAppVersion.realName }
			</div>
		</div>		
		<div class="control-group">
			<label class="control-label">備註:</label>
			<div class="controls">
				<form:textarea path="remarks" htmlEscape="false" rows="4" maxlength="255" class="input-xxlarge "/>
			</div>
		</div>
		<div class="form-actions">
			<shiro:hasPermission name="safe:fire:safeFireAppVersion:edit"><input id="btnSubmit" class="btn btn-primary" type="submit" value="保 存"/> </shiro:hasPermission>
			<input id="btnCancel" class="btn" type="button" value="返 回" onclick="history.go(-1)"/>
		</div>
	</form:form>
        通過觀察程式碼可以發現,path屬性的名稱與這個實體物件的屬性相對應且名稱相同,提交表單時會自動把這些屬性的值存到modelAttribute中傳到後臺,我們不可以直接選擇檔案後就直接得到相對路徑存到modelAttribute中,只有經過後臺處理後才可以進行儲存,所以,在頁面中檔案只是一個獨立的檔案而已,path的值可以不填甚至不要這個屬性,但是name屬性是必須要用到的,這是用於後臺識別檔案的唯一標識。這就好比採摘水果,傳統售賣摘下來就可以拿去賣了,因為客戶需要的只是普通水果,而果汁飲料需要採摘下來再加工後做成飲料產品才能滿足客戶需求。

        表單提交的方式為POST,我們只要在後臺利用Springmvc的@RequestParam註解就可以對前臺傳送過來的檔案進行接收了。後臺程式碼如下:

@RequiresPermissions("safe:fire:safeFireAppVersion:edit")
	@RequestMapping(value = "save")
	public String save(@RequestParam("apkFile") MultipartFile apkFile, SafeFireAppVersion safeFireAppVersion, Model model, RedirectAttributes redirectAttributes) throws Exception {
		if (!beanValidator(model, safeFireAppVersion)) {
			return form(safeFireAppVersion, model);
		}
		if (!apkFile.isEmpty()) { // 判斷檔案是否為空
			String oldPath = null;
			if(!safeFireAppVersion.getIsNewRecord()){ // 判斷是否是新記錄,不是的話就是修改中的儲存
				SafeFireAppVersion old = safeFireAppVersionService.get(safeFireAppVersion); // 獲取原先的檔案
				FileUtils.delFileByPath(old.getUrl());	// 刪除原檔案
				oldPath = old.getUrl(); // 獲取原先的檔案的路徑,用於保留之前的uuid,換了檔案還是用之前的uuid作為加密後的檔名
			}
			String filePath = UploadUtils.appVersionUpload(FireConstant.UPLOAD_VERSION_PATH, apkFile, oldPath); // 開始檔案上傳
			safeFireAppVersion.setUrl(filePath);
			safeFireAppVersion.setRealName(apkFile.getOriginalFilename()); // 真實名稱
		}
		safeFireAppVersionService.save(safeFireAppVersion);
		addMessage(redirectAttributes, "儲存滅火器APP版本資訊成功");
		return "redirect:" + Global.getAdminPath() + "/safe/fire/safeFireAppVersion/?repage";
	}