帶有進度條的fastdfs文件下載的demo,也有上傳,刪除文件的功能

通過fastdfs-java-client的api按塊下載文件,下載成功后寫入到輸出流并將進度按用戶通過websocket推送到客戶端

注:該demo只是單純實現(xiàn)了有進度條的下載,如果下載的接口不做其它處理用戶會卡在下載進程里,后續(xù)再做處理

源碼git地址:https://github.com/xujun738/spring-uploadfile.git這里的代碼已經(jīng)在用戶請求后另起一個線程進行下載處理

1.pom.xml

<!—fastdfs-client—>

<dependency>

?? <groupId>com.github.tobato</groupId>

?? <artifactId>fastdfs-client</artifactId>

?? <version>1.26.5</version>

</dependency>

<!—springboot ?websocket—>

<dependency>

?? <groupId>org.springframework.boot</groupId>

?? <artifactId>spring-boot-starter-websocket</artifactId>

</dependency>

springboot集成fastdfs

配置application.yml

fdfs:

? so-timeout: 600000

? connectTimeout: 600

? thumbImage:???????????? #縮略圖生成參數(shù)

??? width: 150

??? height: 150

? pool:

??? jmx-enabled: false

??? max-total: 200

??? max-wait-millis: 30000

? trackerList:??????????? #TrackerList參數(shù),支持多個

??? - ip:22122?? # 107.182.180.143:22122

? visit:

url:http://ip? #47.100.116.208

??? port: 8888

Websocket配置類

@Configuration

public class WebSocketConfig {

??? @Bean

??? public ServerEndpointExporter serverEndpointExporter() {

??????? return new ServerEndpointExporter();

??? }

}

websocket服務類

/**

* <p>Description : </p>

* <p>Copyright : Copyright (c) 2018</p>

* <p>Company : tgram </p>

*

* @author eric

* @version 1.0

* @Date 2019/3/8 上午9:57

*/

@ServerEndpoint("/websocket/{userId}")

@Component

public class MyWebSocket {

??? //靜態(tài)變量德澈,用來記錄當前在線連接數(shù)窃爷。應該把它設計成線程安全的屑埋。

??? private static int onlineCount = 0;

??? //concurrent包的線程安全Set爱咬,用來存放每個客戶端對應的MyWebSocket對象筐摘。

??? private static CopyOnWriteArraySet<MyWebSocket> webSocketSet = new CopyOnWriteArraySet<MyWebSocket>();

??? //與某個客戶端的連接會話范抓,需要通過它來給客戶端發(fā)送數(shù)據(jù)

??? private Session session;

??? private String userId = null;

??? /**

???? * 連接建立成功調(diào)用的方法

???? */

??? @OnOpen

??? public void onOpen(Session session, @PathParam("userId") String userId) {

??????? this.session = session;

??????? this.userId = userId;

??????? webSocketSet.add(this);???? //加入set中

??????? addOnlineCount();?????????? //在線數(shù)加1

??????? System.out.println("有新連接加入蘸泻!當前在線人數(shù)為" + getOnlineCount());

??????? try {

??????????? sendMessage("連接成功");

??????? } catch (IOException e) {

??????????? System.out.println("IO異常");

??????? }

??? }

??? /**

???? * 連接關(guān)閉調(diào)用的方法

???? */

??? @OnClose

??? public void onClose() {

??????? webSocketSet.remove(this);? //從set中刪除

??????? subOnlineCount();?????????? //在線數(shù)減1

??????? System.out.println("有一連接關(guān)閉!當前在線人數(shù)為" + getOnlineCount());

??? }

??? /**

???? * 收到客戶端消息后調(diào)用的方法

???? *

???? * @param message 客戶端發(fā)送過來的消息

???? */

??? @OnMessage

??? public void onMessage(String message, Session session) {

??????? System.out.println("來自客戶端的消息:" + message);

??????? //群發(fā)消息

??????? for (MyWebSocket item : webSocketSet) {

??????????? try {

??????????????? item.sendMessage(message);

??????????? } catch (IOException e) {

??????????????? e.printStackTrace();

??????????? }

??????? }

??? }

??? /**

???? * 發(fā)生錯誤時調(diào)用

???? *

???? * @OnError public void onError(Session session, Throwable error) {

???? * System.out.println("發(fā)生錯誤");

???? * error.printStackTrace();

???? * }

???? * <p>

???? * <p>

???? * public void sendMessage(String message) throws IOException {

???? * this.session.getBasicRemote().sendText(message);

???? * //this.session.getAsyncRemote().sendText(message);

???? * }

???? * <p>

???? * <p>

???? * /**

???? * 群發(fā)自定義消息

???? */

??? public static void sendInfo(String message, @PathParam("userId") String userId) throws IOException {

??????? for (MyWebSocket item : webSocketSet) {

??????????? try {

??????????????? //這里可以設定只推送給這個userId的脱篙,為null則全部推送

??????????????? if (userId == null) {

??????????????????? item.sendMessage(message);

??????????????? } else if (item.userId.equals(userId)) {

??????????????????? item.sendMessage(message);

??????????????? }

??????????? } catch (IOException e) {

??????????????? continue;

??????????? }

??????? }

??? }

??? /**

???? * 實現(xiàn)服務器主動推送

???? */

??? public void sendMessage(String message) throws IOException {

??????? this.session.getBasicRemote().sendText(message);

??? }

??? public static synchronized int getOnlineCount() {

??????? return onlineCount;

??? }

??? public static synchronized void addOnlineCount() {

??????? MyWebSocket.onlineCount++;

??? }

??? public static synchronized void subOnlineCount() {

??????? MyWebSocket.onlineCount--;

??? }

}

5.控制層類

@Controller

@RequestMapping("/upload")

public class UploadCtrl {

??? @Autowired

??? private FastDFSClientWrapper fastDFSClientWrapper;

??? @Autowired

??? private FastFileStorageClient storageClient;

??? @RequestMapping(value = "", method = RequestMethod.POST)

??? @ResponseBody

??? public InfoMsg fileUpload(@RequestParam("uploadFile") MultipartFile file) {

??????? InfoMsg infoMsg = new InfoMsg();

??????? if (file.isEmpty()) {

??????????? infoMsg.setCode("error");

??????????? infoMsg.setMsg("Please select a file to upload");

??????????? return infoMsg;

??????? }

??????? try {

??????????? String url = fastDFSClientWrapper.uploadFile(file);

??????????? System.out.println("上傳的文件URL:?? " + url);

??????????? JSONObject jsonObject = new JSONObject();

??????????? jsonObject.put("url", url);

??????????? jsonObject.put("filesize", file.getSize());

//?????? File tmp = new File(TMP_PATH, file.getOriginalFilename());

//?????? if(!tmp.getParentFile().exists()){

//????????? tmp.getParentFile().mkdirs();

//?????? }

//?????? file.transferTo(tmp);

??????????? infoMsg.setCode("success");

??????????? infoMsg.setMsg("You successfully uploaded '" + file.getOriginalFilename() + "'");

??????? } catch (IOException e) {

??????????? infoMsg.setCode("error");

??????????? infoMsg.setMsg("Uploaded file failed");

??????? }

??????? return infoMsg;

??? }

??? @RequestMapping(value = "/delete", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")

??? @ResponseBody

??? public String deleteFile(String fileName) {

??????? try {

??????????? fastDFSClientWrapper.deleteFile(fileName);

??????????? return "刪除成功";

??????? } catch (Exception e) {

??????????? e.printStackTrace();

??????????? return "刪除失敗";

??????? }

??? }

??? @RequestMapping(value = "/download")

??? @ResponseBody

??? public void download(String fileName, HttpServletResponse response, String userId) throws IOException {

??????? StorePath storePath = StorePath.parseFromUrl(fileName);

??????? // 配置文件下載

??????? response.setHeader("content-type", "application/octet-stream");

??????? response.setContentType("application/octet-stream");

??????? // 下載文件能正常顯示中文

??????? response.setHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode(fileName, "UTF-8"));

//??????? byte[] r = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), new DownloadCallback<byte[]>() {

//??????????? @Override

//??????????? public byte[] recv(InputStream ins) throws IOException {

//??????????????? byte[] reulst = IOUtils.toByteArray(ins);

//??????????????? System.out.println(reulst.length);

//??????????????? return reulst;

//??????????? }

//??????? });

//??????? response.getOutputStream().write(r);

??????? FileInfo fileInfo = storageClient.queryFileInfo(storePath.getGroup(), storePath.getPath());

??????? long fileSize = fileInfo.getFileSize();

??????? System.out.println("文件總大小:" + fileSize);

??????? long slice = Math.floorDiv(fileSize, 100);

??????? long left = fileSize - slice * 99;

??????? byte[] sliceBytes = null;

??????? int downloadBytes = 0;

??????? ByteBuffer bb = new ByteBuffer();

??????? for (int i = 0; i < 100; i++) {

??????????? if (i != 99) {

??????????????? sliceBytes = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), i * slice, slice, ins -> {

??????????????????? byte[] result = IOUtils.toByteArray(ins);

??????????????????? response.getOutputStream().write(result);

??????????????????? return result;

??????????????? });

??????????? } else {

??????????????? sliceBytes = storageClient.downloadFile(storePath.getGroup(), storePath.getPath(), 99 * slice, left, ins -> {

??????????????????? byte[] result = IOUtils.toByteArray(ins);

??????????????????? response.getOutputStream().write(result);

??????????????????? return result;

??????????????? });

??????????? }

??????????? downloadBytes = downloadBytes + sliceBytes.length;

??????????? MyWebSocket.sendInfo((i + 1) + "", userId);

??????? }

??????? response.getOutputStream().flush();

//??????? 新起一個線程,然后按段下載文件,每段下載成功后將進度值推送到對應的用戶

??????? System.out.println("共下載:" + downloadBytes);

??? }

}

服務啟動類

@SpringBootApplication

@Import(FdfsClientConfig.class)

@EnableAutoConfiguration

public class StudyApplication {

?? public static void main(String[] args) {

????? new SpringApplication(StudyApplication.class).run(args);

?? }

}

7.html頁面

<!DOCTYPE HTML>

<html>

<head>

????<title>My WebSocket</title>

</head>

<body>

Welcome<br/>

<input id="text" type="text" /><button onclick="send()">Send</button>????<button onclick="closeWebSocket()">Close</button>

<div id="message">

</div>

</body>

<script type="text/javascript">

????var websocket = null;

????//判斷當前瀏覽器是否支持WebSocket

????if('WebSocket' in window){

websocket = new WebSocket("ws://localhost:8080/websocket/admin1");

????}

????else{

????????alert('Not support websocket')

????}

????//連接發(fā)生錯誤的回調(diào)方法

????websocket.onerror = function(){

????????setMessageInnerHTML("error");

????};

????//連接成功建立的回調(diào)方法

????websocket.onopen = function(event){

????????setMessageInnerHTML("open");

????}

????//接收到消息的回調(diào)方法

????websocket.onmessage = function(event){

????????setMessageInnerHTML(event.data);

????}

????//連接關(guān)閉的回調(diào)方法

????websocket.onclose = function(){

????????setMessageInnerHTML("close");

????}

????//監(jiān)聽窗口關(guān)閉事件莹弊,當窗口關(guān)閉時涤久,主動去關(guān)閉websocket連接,防止連接還沒斷開就關(guān)閉窗口忍弛,server端會拋異常响迂。

????window.onbeforeunload = function(){

????????websocket.close();

????}

????//將消息顯示在網(wǎng)頁上

????function setMessageInnerHTML(innerHTML){

????????document.getElementById('message').innerHTML += innerHTML + '<br/>';

????}

????//關(guān)閉連接

????function closeWebSocket(){

????????websocket.close();

????}

????//發(fā)送消息

????function send(){

????????var message = document.getElementById('text').value;

????????websocket.send(message);

????}

</script>

</html>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市细疚,隨后出現(xiàn)的幾起案子蔗彤,更是在濱河造成了極大的恐慌,老刑警劉巖疯兼,帶你破解...
    沈念sama閱讀 212,222評論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件然遏,死亡現(xiàn)場離奇詭異,居然都是意外死亡吧彪,警方通過查閱死者的電腦和手機待侵,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,455評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來姨裸,“玉大人秧倾,你說我怎么就攤上這事】酰” “怎么了那先?”我有些...
    開封第一講書人閱讀 157,720評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赡艰。 經(jīng)常有香客問我售淡,道長,這世上最難降的妖魔是什么慷垮? 我笑而不...
    開封第一講書人閱讀 56,568評論 1 284
  • 正文 為了忘掉前任揖闸,我火速辦了婚禮,結(jié)果婚禮上料身,老公的妹妹穿的比我還像新娘楔壤。我一直安慰自己,他們只是感情好惯驼,可當我...
    茶點故事閱讀 65,696評論 6 386
  • 文/花漫 我一把揭開白布蹲嚣。 她就那樣靜靜地躺著,像睡著了一般祟牲。 火紅的嫁衣襯著肌膚如雪隙畜。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,879評論 1 290
  • 那天说贝,我揣著相機與錄音议惰,去河邊找鬼。 笑死乡恕,一個胖子當著我的面吹牛言询,可吹牛的內(nèi)容都是我干的俯萎。 我是一名探鬼主播,決...
    沈念sama閱讀 39,028評論 3 409
  • 文/蒼蘭香墨 我猛地睜開眼运杭,長吁一口氣:“原來是場噩夢啊……” “哼夫啊!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起辆憔,我...
    開封第一講書人閱讀 37,773評論 0 268
  • 序言:老撾萬榮一對情侶失蹤撇眯,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后虱咧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體熊榛,經(jīng)...
    沈念sama閱讀 44,220評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,550評論 2 327
  • 正文 我和宋清朗相戀三年腕巡,在試婚紗的時候發(fā)現(xiàn)自己被綠了玄坦。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,697評論 1 341
  • 序言:一個原本活蹦亂跳的男人離奇死亡绘沉,死狀恐怖煎楣,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情梆砸,我是刑警寧澤转质,帶...
    沈念sama閱讀 34,360評論 4 332
  • 正文 年R本政府宣布园欣,位于F島的核電站帖世,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏沸枯。R本人自食惡果不足惜日矫,卻給世界環(huán)境...
    茶點故事閱讀 40,002評論 3 315
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望绑榴。 院中可真熱鬧哪轿,春花似錦、人聲如沸翔怎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,782評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽赤套。三九已至飘痛,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間容握,已是汗流浹背宣脉。 一陣腳步聲響...
    開封第一講書人閱讀 32,010評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留剔氏,地道東北人塑猖。 一個月前我還...
    沈念sama閱讀 46,433評論 2 360
  • 正文 我出身青樓竹祷,卻偏偏與公主長得像,于是被迫代替她去往敵國和親羊苟。 傳聞我的和親對象是個殘疾皇子塑陵,可洞房花燭夜當晚...
    茶點故事閱讀 43,587評論 2 350

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

  • JAVA面試題 1、作用域public,private,protected,以及不寫時的區(qū)別答:區(qū)別如下:作用域 ...
    JA尐白閱讀 1,146評論 1 0
  • 原文地址:http://www.ibm.com/developerworks/cn/java/j-lo-WebSo...
    敢夢敢當閱讀 8,892評論 0 50
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,805評論 0 11
  • 小編費力收集:給你想要的面試集合 1.C++或Java中的異常處理機制的簡單原理和應用践险。 當JAVA程序違反了JA...
    八爺君閱讀 4,580評論 1 114
  • 姓名:易平香 企業(yè)名稱:東莞耀升機電有限公司 組別:AT感謝組/272期努力一組 【日精進打卡第70天】 【知~學...
    shine1yi閱讀 164評論 0 0