昨天忘記給大家說了個(gè)事,昨天添加FTP服務(wù)器依賴那部分我在搭建項(xiàng)目開始時(shí)就已經(jīng)在taotao-common的pom文件下寫好了,大家可以回去看看里面添加Apache組件那部分里面就有。
1、Nginx+FTP出現(xiàn)403錯(cuò)誤
還有就是訪問nginx下的ftp圖片會(huì)有我遇到的這個(gè)問題,如圖:
我不知道大家有沒有遇到,假如遇到也不知道大家是怎么去解決的,我把我的解決可以具體點(diǎn)寫出來。
1.1、錯(cuò)誤分析:
我看了下網(wǎng)上的關(guān)于這方面的相關(guān)資料,大概總結(jié)的兩個(gè)可能的原因:
1、缺少index.html或者index.php文件(索引文件)
2、權(quán)限問題
這是我nginx修改后的配置文件
因此我現(xiàn)在直接排除掉第一種情況。
怎么說呢。果然大部分原因還是會(huì)出現(xiàn)在第二種情況下,這就要我們需要去熟悉linux了,可憐的小伙伴們不知道學(xué)習(xí)的咋樣。先解決問題在討論學(xué)習(xí)方面吧。
1.2、錯(cuò)誤解決
一般這種情況我的是因?yàn)槭窃趓oot用戶下編譯的安裝及啟動(dòng)nginx的,會(huì)出現(xiàn)權(quán)限問題,沒跟ftp服務(wù)器所屬用戶一致導(dǎo)致。于是我就改變了nginx所屬的用戶和用戶組。
- root@cdh4>chmod 777 /home/ftpuser/www
- root@cdh4>chown -R ftpuser:ftpuser /usr/local/nginx
- root@cdh4>/usr/local/nginx/sbin/nginx -s reload
之后在打開瀏覽器就可以訪問的到我昨天用測(cè)試代碼上傳的圖片了,地址:
http://blog.csdn.net/sinat_31726559/article/details/52153330如果還是不行就重啟一下機(jī)器,在關(guān)閉好iptables或者firewall就可以了
2、訪問Nginx圖片失真
上面雖然我們能夠訪問到圖片了,但是圖片卻存在失真的情況,這又是怎么一回事呢?
2.1、錯(cuò)誤分析
先看看昨天我寫的測(cè)試代碼
從上面可以看出我上傳到ftp服務(wù)器是以字節(jié)流傳輸?shù)?,到服?wù)器后是文本格式,而圖片是二進(jìn)制格式,所以上傳上去或出現(xiàn)編碼不能恢復(fù)到原來的圖片模樣。
2.2、錯(cuò)誤解決
知道錯(cuò)誤原因就好解決了,只要修改上傳文件的格式就行了,添加以下一句代碼就ok了
- //修改上傳文件格式
- client.setFileType(FTP.BINARY_FILE_TYPE);
將之前上傳的圖片先從服務(wù)器刪除,在用java代碼上傳一回
上傳ok,接著我們?cè)偎⑿乱幌聻g覽器看看效果。
呵呵,搞定!
3、項(xiàng)目所用到的FTP工具類
這里因?yàn)槭琼?xiàng)目中所用到的工具類??紤]到代碼的復(fù)用性,我就在taotao-common下新建的一個(gè)utils的工具類的包,如下:
工具類代碼如下:
代碼一
- package com.taotao.common.utils;
-
- import java.io.File;
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import java.io.InputStream;
- import java.io.OutputStream;
-
- import org.apache.commons.net.ftp.FTP;
- import org.apache.commons.net.ftp.FTPClient;
- import org.apache.commons.net.ftp.FTPFile;
- import org.apache.commons.net.ftp.FTPReply;
-
- /**
- *
- * @ClassName: FtpUtil
- * @Description: TODO(ftp服務(wù)器的工具類)
- * @author 汪本成
- * @date 2016年8月9日 上午10:43:38
- *
- */
- public class FtpUtil {
-
- /**
- * Description: 向FTP服務(wù)器上傳文件
- * @param host FTP服務(wù)器hostname
- * @param port FTP服務(wù)器端口
- * @param username FTP登錄賬號(hào)
- * @param password FTP登錄密碼
- * @param basePath FTP服務(wù)器基礎(chǔ)目錄
- * @param filePath FTP服務(wù)器文件存放路徑。例如分日期存放:/2015/01/01。文件的路徑為basePath+filePath
- * @param filename 上傳到FTP服務(wù)器上的文件名
- * @param input 輸入流
- * @return 成功返回true,否則返回false
- */
- public static boolean uploadFile(String host, int port, String username, String password, String basePath,
- String filePath, String filename, InputStream input) {
- boolean result = false;
- FTPClient ftp = new FTPClient();
- try {
- int reply;
- ftp.connect(host, port);// 連接FTP服務(wù)器
- // 如果采用默認(rèn)端口,可以使用ftp.connect(host)的方式直接連接FTP服務(wù)器
- ftp.login(username, password);// 登錄
- reply = ftp.getReplyCode();
- if (!FTPReply.isPositiveCompletion(reply)) {
- ftp.disconnect();
- return result;
- }
- //切換到上傳目錄
- if (!ftp.changeWorkingDirectory(basePath+filePath)) {
- //如果目錄不存在創(chuàng)建目錄
- String[] dirs = filePath.split("/");
- String tempPath = basePath;
- for (String dir : dirs) {
- if (null == dir || "".equals(dir)) continue;
- tempPath += "/" + dir;
- if (!ftp.changeWorkingDirectory(tempPath)) {
- if (!ftp.makeDirectory(tempPath)) {
- return result;
- } else {
- ftp.changeWorkingDirectory(tempPath);
- }
- }
- }
- }
- //設(shè)置上傳文件的類型為二進(jìn)制類型
- ftp.setFileType(FTP.BINARY_FILE_TYPE);
- //上傳文件
- if (!ftp.storeFile(filename, input)) {
- return result;
- }
- input.close();
- ftp.logout();
- result = true;
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (ftp.isConnected()) {
- try {
- ftp.disconnect();
- } catch (IOException ioe) {
- }
- }
- }
- return result;
- }
-
- /**
- * Description: 從FTP服務(wù)器下載文件
- * @param host FTP服務(wù)器hostname
- * @param port FTP服務(wù)器端口
- * @param username FTP登錄賬號(hào)
- * @param password FTP登錄密碼
- * @param remotePath FTP服務(wù)器上的相對(duì)路徑
- * @param fileName 要下載的文件名
- * @param localPath 下載后保存到本地的路徑
- * @return
- */
- public static boolean downloadFile(String host, int port, String username, String password, String remotePath,
- String fileName, String localPath) {
- boolean result = false;
- FTPClient ftp = new FTPClient();
- try {
- int reply;
- ftp.connect(host, port);
- // 如果采用默認(rèn)端口,可以使用ftp.connect(host)的方式直接連接FTP服務(wù)器
- ftp.login(username, password);// 登錄
- reply = ftp.getReplyCode();
- if (!FTPReply.isPositiveCompletion(reply)) {
- ftp.disconnect();
- return result;
- }
- ftp.changeWorkingDirectory(remotePath);// 轉(zhuǎn)移到FTP服務(wù)器目錄
- FTPFile[] fs = ftp.listFiles();
- for (FTPFile ff : fs) {
- if (ff.getName().equals(fileName)) {
- File localFile = new File(localPath + "/" + ff.getName());
-
- OutputStream is = new FileOutputStream(localFile);
- ftp.retrieveFile(ff.getName(), is);
- is.close();
- }
- }
-
- ftp.logout();
- result = true;
- } catch (IOException e) {
- e.printStackTrace();
- } finally {
- if (ftp.isConnected()) {
- try {
- ftp.disconnect();
- } catch (IOException ioe) {
- }
- }
- }
- return result;
- }
-
- public static void main(String[] args) {
- try {
- FileInputStream in=new FileInputStream(new File("D:\\temp\\image\\gaigeming.jpg"));
- boolean flag = uploadFile("192.168.25.133", 21, "ftpuser", "ftpuser", "/home/ftpuser/www/images","/2015/01/21", "gaigeming.jpg", in);
- System.out.println(flag);
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- }
- }
這個(gè)工具類的測(cè)試代碼就讓你們寫寫了,不會(huì)可以給我留言,及時(shí)給你答復(fù)
。
4、圖片上傳的實(shí)現(xiàn)
4.1、需求分析
Common.js
1、綁定事件,上傳圖片的組件
2、初始化參數(shù)
2、上傳圖片的url:
/pic/upload
3、上圖片參數(shù)名稱:
uploadFile
4、返回結(jié)果數(shù)據(jù)類型json
參考文檔:http://kindeditor.net/docs/upload.html
返回格式(JSON)
- //成功時(shí)
- {
- "error" : 0,
- "url" : "http://www.example.com/path/to/file.ext"
- }
- //失敗時(shí)
- {
- "error" : 1,
- "message" : "錯(cuò)誤信息"
- }
5、整個(gè)組建關(guān)鍵代碼
代碼二
- var TT = TAOTAO = {
- // 編輯器參數(shù)
- kingEditorParams : {
- //指定上傳文件參數(shù)名稱
- filePostName : "uploadFile",
- //指定上傳文件請(qǐng)求的url。
- uploadJson : '/pic/upload',
- //上傳類型,分別為image、flash、media、file
- dir : "image"
- },
- // 格式化時(shí)間
- formatDateTime : function(val,row){
- var now = new Date(val);
- return now.format("yyyy-MM-dd hh:mm:ss");
- },
- // 格式化連接
- formatUrl : function(val,row){
- if(val){
- return "<a href='"+val+"' target='_blank'>查看</a>";
- }
- return "";
- },
- // 格式化價(jià)格
- formatPrice : function(val,row){
- return (val/1000).toFixed(2);
- },
- // 格式化商品的狀態(tài)
- formatItemStatus : function formatStatus(val,row){
- if (val == 1){
- return '正常';
- } else if(val == 2){
- return '<span style="color:red;">下架</span>';
- } else {
- return '未知';
- }
- },
-
- init : function(data){
- // 初始化圖片上傳組件
- this.initPicUpload(data);
- // 初始化選擇類目組件
- this.initItemCat(data);
- },
- // 初始化圖片上傳組件
- initPicUpload : function(data){
- $(".picFileUpload").each(function(i,e){
- var _ele = $(e);
- _ele.siblings("div.pics").remove();
- _ele.after('\
- <div class="pics">\
- <ul></ul>\
- </div>');
- // 回顯圖片
- if(data && data.pics){
- var imgs = data.pics.split(",");
- for(var i in imgs){
- if($.trim(imgs[i]).length > 0){
- _ele.siblings(".pics").find("ul").append("<li><a href='"+imgs[i]+"' target='_blank'><img src='"+imgs[i]+"' width='80' height='50' /></a></li>");
- }
- }
- }
- //給“上傳圖片按鈕”綁定click事件
- $(e).click(function(){
- var form = $(this).parentsUntil("form").parent("form");
- //打開圖片上傳窗口
- KindEditor.editor(TT.kingEditorParams).loadPlugin('multiimage',function(){
- var editor = this;
- editor.plugin.multiImageDialog({
- clickFn : function(urlList) {
- var imgArray = [];
- KindEditor.each(urlList, function(i, data) {
- imgArray.push(data.url);
- form.find(".pics ul").append("<li><a href='"+data.url+"' target='_blank'><img src='"+data.url+"' width='80' height='50' /></a></li>");
- });
- form.find("[name=image]").val(imgArray.join(","));
- editor.hideDialog();
- }
- });
- });
- });
- });
- },
詳細(xì)的記得要查看我上面發(fā)給你的鏈接資料喲。
4.2、Service
功能:接收controller層傳遞過來的圖片對(duì)象,把圖片上傳到ftp服務(wù)器。給圖片生成一個(gè)新的名字,防止文件名重復(fù)。返回文件的url路徑。需要保證圖片上傳插件的數(shù)據(jù)方式。
這里有兩種實(shí)現(xiàn)方式:
1、創(chuàng)建一個(gè)pojo對(duì)象來實(shí)現(xiàn)
2、創(chuàng)建一個(gè)Map實(shí)現(xiàn)
這里我用Map實(shí)現(xiàn)。
Map中的內(nèi)容:
key | Value |
Error | 1、0 |
URL | 圖片的url(成功時(shí)) |
Message | 錯(cuò)誤信息(失敗時(shí)) |
首先去service里面定義一個(gè)接口,為PictureService
代碼三
- package com.taotao.service;
-
- import java.util.Map;
-
- import org.springframework.web.multipart.MultipartFile;
-
- /**
- *
- * @ClassName: PictureService
- * @Description: TODO(圖片上傳接口)
- * @author 汪本成
- * @date 2016年8月9日 下午12:01:27
- *
- */
- public interface PictureService {
-
- Map<?, ?> uploadFile(MultipartFile uploadFile);
-
- }
對(duì)接口進(jìn)行實(shí)現(xiàn),但是實(shí)現(xiàn)時(shí)候我們得先整理好我們的思路。
1、對(duì)生成的文件名要保證能夠不進(jìn)行重復(fù),開始我想到的是UUID,但是感覺太長(zhǎng)了。就用一個(gè)生成id的工具類解決,代碼如下:
代碼四
- package com.taotao.common.utils;
-
- import java.util.Random;
-
- /**
- *
- * @ClassName: IDUtils
- * @Description: TODO(各種id生成策略)
- * @author 汪本成
- * @date 2016年8月9日 下午12:40:19
- *
- */
- public class IDUtils {
-
- /**
- * 圖片名生成
- */
- public static String genImageName() {
- //取當(dāng)前時(shí)間的長(zhǎng)整形值包含毫秒
- long millis = System.currentTimeMillis();
- //long millis = System.nanoTime();
- //加上三位隨機(jī)數(shù)
- Random random = new Random();
- int end3 = random.nextInt(999);
- //如果不足三位前面補(bǔ)0
- String str = millis + String.format("%03d", end3);
-
- return str;
- }
-
- /**
- * 商品id生成
- */
- public static long genItemId() {
- //取當(dāng)前時(shí)間的長(zhǎng)整形值包含毫秒
- long millis = System.currentTimeMillis();
- //long millis = System.nanoTime();
- //加上兩位隨機(jī)數(shù)
- Random random = new Random();
- int end2 = random.nextInt(99);
- //如果不足兩位前面補(bǔ)0
- String str = millis + String.format("%02d", end2);
- long id = new Long(str);
- return id;
- }
-
- public static void main(String[] args) {
- for(int i=0;i< 100;i++)
- System.out.println(genItemId());
- }
- }
2、進(jìn)行圖片上傳時(shí),我們首先考慮到不能綁定死一個(gè)機(jī)器,在代碼中就決定或者說寫死這個(gè)機(jī)器信息,應(yīng)該在配置文件里進(jìn)行配置,在讀取配置文件信息會(huì)比較好,于是新建一個(gè)properties文件來記錄信息。這里我們絕對(duì)不能將這個(gè)配置文件寫到j(luò)ar包工程下,所以在taotao-manager-web工程下的resource文件夾下新建一個(gè)resource.properties文件來保存配置信息,如下:
代碼五
- #FTP相關(guān)配置
- #FTP ip地址
- FTP_IP=192.168.43.163
- FTP_PORT=21
- FTP_USERNAME=ftpuser
- FTP_PASSWORD=115010
- FTP_BASE_PATH=/home/ftpuser/www/images
- #圖片服務(wù)器的相關(guān)配置
- #圖片服務(wù)器的基礎(chǔ)url
- IMAGE_BASE_URL=http://192.168.43.163/images
然后接下來考慮怎么讀取這個(gè)配置文件,到這步我們可以回憶一下之前我們是怎么讀取數(shù)據(jù)庫(kù)的配置文件db.properties的。spring給我們提供了完整的解決方案,不會(huì)的伙伴spring可得好好學(xué)了喲。考慮細(xì)節(jié),這里我就多說一點(diǎn)吧。
spring讀取信息這部分是在之前寫的xml文件里,這里我截個(gè)圖給大家看下大家就明白了。
然后就是spring讀取文件信息了,這里就要用到@Value("${文件的key字段}")這個(gè)知識(shí)點(diǎn)了,當(dāng)你在寫java代碼聲明這個(gè)字段的時(shí)候spring會(huì)給你自動(dòng)注入進(jìn)去的。好,直接來給大家寫好代碼,畢竟得要干貨嘛。
代碼六
- package com.taotao.service.impl;
-
- import java.io.IOException;
- import java.util.HashMap;
- import java.util.Map;
- import java.util.UUID;
-
- import org.joda.time.DateTime;
- import org.springframework.beans.factory.annotation.Value;
- import org.springframework.stereotype.Service;
- import org.springframework.web.multipart.MultipartFile;
-
- import com.taotao.common.utils.FtpUtil;
- import com.taotao.common.utils.IDUtils;
- import com.taotao.service.PictureService;
-
- /**
- *
- * @ClassName: PictureServiceImpl
- * @Description: TODO(圖片上傳服務(wù))
- * @author 汪本成
- * @date 2016年8月9日 下午12:02:33
- *
- */
- @Service
- public class PictureServiceImpl implements PictureService {
-
- //注入resource.properties的Key字段
- @Value("${FTP_IP}")
- private String FTP_IP;
- @Value("${FTP_PORT}")
- private Integer FTP_PORT;
- @Value("${FTP_USERNAME}")
- private String FTP_USERNAME;
- @Value("${FTP_PASSWORD}")
- private String FTP_PASSWORD;
- @Value("${FTP_BASE_PATH}")
- private String FTP_BASE_PATH;
- @Value("${IMAGE_BASE_URL}")
- private String IMAGE_BASE_URL;
-
- @Override
- public Map<?, ?> uploadFile(MultipartFile uploadFile) {
- Map resultMap = new HashMap<>();
- try {
- //生成一個(gè)新的文件名
- //取原始文件名
- String oldName = uploadFile.getOriginalFilename();
-
- //生成新文件名
- //UUID.randomUUID();
- String newName = IDUtils.genImageName();
- newName = newName + oldName.substring(oldName.lastIndexOf("."));
-
- //圖片上傳
- String imagePath = new DateTime().toString("/yyyy/MM/dd");
- boolean result = FtpUtil.uploadFile(FTP_IP, FTP_PORT, FTP_USERNAME, FTP_PASSWORD, FTP_BASE_PATH,
- imagePath, newName, uploadFile.getInputStream());
-
- //返回結(jié)果
- if(!result) {
- resultMap.put("error", 1);
- resultMap.put("message", "文件上傳失敗");
- return resultMap;
-
- }
- resultMap.put("error", 0);
- resultMap.put("url", IMAGE_BASE_URL + imagePath + "/" + newName);
- return resultMap;
-
- } catch (IOException e) {
- resultMap.put("error", 1);
- resultMap.put("message", "文件上傳異常");
- return resultMap;
- }
-
- }
-
- }
4.3、Controller
功能:接收頁面?zhèn)鬟f過來的圖片。調(diào)用service上傳到圖片服務(wù)器。返回結(jié)果。
參數(shù):MultiPartFileuploadFile
返回值:返回json數(shù)據(jù),應(yīng)該返回一個(gè)pojo,PictureResult對(duì)象。
代碼七
- package com.taotao.controller;
-
- import java.util.Map;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.multipart.MultipartFile;
-
- import com.taotao.service.PictureService;
-
- @Controller
- public class PictureController {
-
- @Autowired
- private PictureService pictureService;
-
- @RequestMapping("/pic/upload")
- @ResponseBody
- public Map<?, ?> pictureUpload(MultipartFile uploadFile) {
- Map<?, ?> result = pictureService.uploadFile(uploadFile);
- return result;
- }
- }
然后更新一下taotao-common這個(gè)工程。啟動(dòng)taotao-manager,點(diǎn)擊上傳圖片。
4.4、圖片上傳異常
控制臺(tái)輸出一下異常信息:
錯(cuò)誤分析:缺少配置文件
錯(cuò)誤解決:1、需要引入file-up;oad和common-io包;
2、在springmvc.xml中配置多部件解析器,添加如下內(nèi)容。
代碼八
- <!-- 定義文件上傳解析器 -->
- <bean id="multipartResolver"
- class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
- <!-- 設(shè)定默認(rèn)編碼 -->
- <property name="defaultEncoding" value="UTF-8"></property>
- <!-- 設(shè)定文件上傳的最大值5MB,5*1024*1024 -->
- <property name="maxUploadSize" value="5242880"></property>
- </bean>
之后重啟taotao-manager,打開qq瀏覽器,測(cè)試下,發(fā)現(xiàn)好使,如圖:
但是很遺憾,在火狐瀏覽器上卻失敗了
這是為什么呢,我覺得這就是插件本身的兼容性問題。但是問題出來了我們必須得解決呀,怎么解決呢?
這里就要換個(gè)思路,統(tǒng)一換成利用json數(shù)據(jù)來返回就ok。這里我就寫個(gè)json的工具類放到taotao-common下。
代碼九
- package com.taotao.common.utils;
-
- import java.util.List;
-
- import com.fasterxml.jackson.core.JsonProcessingException;
- import com.fasterxml.jackson.databind.JavaType;
- import com.fasterxml.jackson.databind.ObjectMapper;
-
- /**
- *
- * @ClassName: JsonUtils
- * @Description: TODO(淘淘商城自定義響應(yīng)結(jié)構(gòu))
- * @author 汪本成
- * @date 2016年8月10日 上午1:32:37
- *
- */
- public class JsonUtils {
-
- // 定義jackson對(duì)象
- private static final ObjectMapper MAPPER = new ObjectMapper();
-
- /**
- * 將對(duì)象轉(zhuǎn)換成json字符串。
- * <p>Title: pojoToJson</p>
- * <p>Description: </p>
- * @param data
- * @return
- */
- public static String objectToJson(Object data) {
- try {
- String string = MAPPER.writeValueAsString(data);
- return string;
- } catch (JsonProcessingException e) {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * 將json結(jié)果集轉(zhuǎn)化為對(duì)象
- *
- * @param jsonData json數(shù)據(jù)
- * @param clazz 對(duì)象中的object類型
- * @return
- */
- public static <T> T jsonToPojo(String jsonData, Class<T> beanType) {
- try {
- T t = MAPPER.readValue(jsonData, beanType);
- return t;
- } catch (Exception e) {
- e.printStackTrace();
- }
- return null;
- }
-
- /**
- * 將json數(shù)據(jù)轉(zhuǎn)換成pojo對(duì)象list
- * <p>Title: jsonToList</p>
- * <p>Description: </p>
- * @param jsonData
- * @param beanType
- * @return
- */
- public static <T>List<T> jsonToList(String jsonData, Class<T> beanType) {
- JavaType javaType = MAPPER.getTypeFactory().constructParametricType(List.class, beanType);
- try {
- List<T> list = MAPPER.readValue(jsonData, javaType);
- return list;
- } catch (Exception e) {
- e.printStackTrace();
- }
-
- return null;
- }
-
- }
然后再修改一下我們寫的controller就行了
代碼十
- package com.taotao.controller;
-
- import java.util.Map;
-
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.stereotype.Controller;
- import org.springframework.web.bind.annotation.RequestMapping;
- import org.springframework.web.bind.annotation.ResponseBody;
- import org.springframework.web.multipart.MultipartFile;
-
- import com.taotao.common.utils.JsonUtils;
- import com.taotao.service.PictureService;
-
- /**
- *
- * @ClassName: PictureController
- * @Description: TODO(圖片上傳的controller)
- * @author 汪本成
- * @date 2016年8月10日 上午1:33:32
- *
- */
- @Controller
- public class PictureController {
-
- @Autowired
- private PictureService pictureService;
-
- @RequestMapping("/pic/upload")
- @ResponseBody
- public String pictureUpload(MultipartFile uploadFile) {
- Map<?, ?> result = pictureService.uploadFile(uploadFile);
-
- //為了保證兼容性,需要把Result轉(zhuǎn)換成json格式的字符串
- String json = JsonUtils.objectToJson(result);
-
- return json;
- }
- }
然后啟動(dòng)進(jìn)行測(cè)試:
ok,完美解決圖片上傳問題,明天繼續(xù)開發(fā)