vue-simple-uploader之后端(ssm)實(shí)現(xiàn)(補(bǔ)坑記)

實(shí)現(xiàn)說(shuō)一下心得:太坑了,接下來(lái)就讓我描述一下我的填坑之路,順便附上代碼。

坑的地方說(shuō)一下,以免讀者錯(cuò)過(guò)精彩部分:要按照vue-simple-uploader的要求實(shí)現(xiàn)斷點(diǎn)續(xù)傳?。?!那么普通的文件上傳以及博客上的斷點(diǎn)續(xù)傳就只能借鑒,無(wú)法抄襲,坑!?。。?/p>

環(huán)境說(shuō)明

前端

  • 工具:VSCode
  • 框架:vue

后端

  • IDE: eclipse
  • 開(kāi)發(fā)語(yǔ)言:java
  • 框架:ssm、maven

資源說(shuō)明

vue-uploader: https://github.com/simple-uploader/vue-uploader

操作

vue-uploader加入前端項(xiàng)目

npm install vue-simple-uploader --save

然后配置main.js

import Vue from 'vue'
import uploader from 'vue-simple-uploader'
import App from './App.vue'
...
Vue.use(uploader)
...

使用

<template>
  <uploader :options="options" class="uploader-example">
    <uploader-unsupport></uploader-unsupport>
    <uploader-drop>
      <p>Drop files here to upload or</p>
      <uploader-btn>select files</uploader-btn>
      <uploader-btn :attrs="attrs">select images</uploader-btn>
      <uploader-btn :directory="true">select folder</uploader-btn>
    </uploader-drop>
    <uploader-list></uploader-list>
  </uploader>
</template>

<script>
  export default {
    data () {
      return {
        options: {
          //nodejs服務(wù)器代碼 https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js 
          target: '//localhost:8080/example/test/upload'
        },
        attrs: {
          accept: 'video/*'
        }
      }
    }
  }
</script>

<style>
...

然后就是服務(wù)器代碼,創(chuàng)建TestController,添加upload方法

...
@Controller
@RequestMapping("test/")
public class TestController{
...
@ResponseBody
@RequestMapping(path = "/upload", method = RequestMethod.POST)
  public void upload(HttpServletRequest request, HttpServletResponse response){
    UploadUtils.upload(request);
    this.success(response, "上傳成功!");
  }
...
}

其中success方法

...
@Controller
@RequestMapping("test/")
public class TestController{
...
  public void success(HttpServletResponse response, Object obj){
    PrintWriter writer = null;
    try {
       writer = response.getWriter();
        writer.write(obj.toString());
    } catch(Exception e){} finally{
      if( writer != null ){
        writer.close();
      }
    }
  }
...

}

UploadUtils內(nèi)容

...
public class UploadUtils{
  public void  upload(HttpServletRequest request){
        String path = "E:/tmp/";
    CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(
                request.getSession().getServletContext());
        if (multipartResolver.isMultipart(request)) {
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) request;
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                // 一次遍歷所有文件
                MultipartFile file = multiRequest.getFile(iter.next().toString());
                if (file != null) {
                    String p = path + "/" + file.getOriginalFilename();
                    // 上傳
                    try {
                        file.transferTo(new File(p));
                        return p;
                    } catch (IllegalStateException e) {
                        e.printStackTrace();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
        return null;
    }
  }
}

按照道理,這里就應(yīng)該講完了,但是,但是!上傳超過(guò)10M的文件就會(huì)有問(wèn)題,原因是你沒(méi)有實(shí)現(xiàn)斷點(diǎn)續(xù)傳?。?!

關(guān)鍵是你知道了,也無(wú)從下手,不過(guò)還好提供了Nodejs版的服務(wù)端代碼,就當(dāng)了一回翻譯君

Nodejs版代碼: https://github.com/simple-uploader/Uploader/tree/develop/samples/Node.js

接下來(lái)就是本人寫(xiě)的Uploader類了,對(duì)應(yīng)的js代碼是uploader-node.js

額,代碼太多,就先放在最后面了。

先講一下用法吧。

使用

  • 定義service
@Service
public UploadService extends Uploader{
  public UploadService(){
    super("E:/tmp/","file");
  }
}
  • 調(diào)用
@Controller
@RequestMapping("test/")
public class TestController{

@Autowired
private UploadService uploadService;
...
@ResponseBody
@RequestMapping(path = "/upload", method = RequestMethod.POST)
  public void upload(HttpServletRequest request, HttpServletResponse response){
      try{
          uploadService.post(request, new Uploader.UploadListener() {
            @Override
          public void callback(String status, String filename, String original_filename, String identifier, String fileType) {
                if(status != null){
                      this.sucess(status);
                }
              }
        }
      }catch(Excetion e){
        e.printStackTrace();
      }
  }
...
}

最后,把success方法里邊的write.close()給注釋掉,不然會(huì)報(bào)錯(cuò),具體原因還不知,但是坑定是與異步有關(guān),這也是一大坑吶。。。

好了,改關(guān)門(mén)放代碼了:

//Uploader.java


import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Iterator;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.io.FilenameUtils;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.multipart.commons.CommonsMultipartResolver;

//https://github.com/simple-uploader/Uploader/blob/develop/samples/Node.js/uploader-node.js
/**
 * 斷點(diǎn)續(xù)傳
 * 
 * @author 隕石墜滅
 *
 */
public class Uploader {
    /**
     * 臨時(shí)文件夾
     */
    private String temporaryFolder;
    /**
     * 最大文件大小
     */
    private Integer maxFileSize = 52428800;
    // private String fileParameterName;

    public Uploader(String temporaryFolder, String fileParameterName) {
        this.temporaryFolder = temporaryFolder;
        File file = new File(temporaryFolder);
        if (!file.exists()) {
            file.mkdirs();
        }
        // if (fileParameterName == null) {
        // this.fileParameterName = "file";
        // } else {
        // this.fileParameterName = fileParameterName;
        // }
    }

    public String cleanIdentifier(String identifier) {
        return identifier.replaceAll("[^0-9A-Za-z_-]", "");
    }

    public String getChunkFilename(int chunkNumber, String identifier) {
        identifier = cleanIdentifier(identifier);
        return new File(temporaryFolder, "uploader-" + identifier + '.' + chunkNumber).getAbsolutePath();
    }

    public String validateRequest(int chunkNumber, int chunkSize, int totalSize, String identifier, String filename,
            Integer fileSize) {
        identifier = cleanIdentifier(identifier);

        if (chunkNumber == 0 || chunkSize == 0 || totalSize == 0 || identifier.length() == 0
                || filename.length() == 0) {
            return "non_uploader_request";
        }
        int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);
        if (chunkNumber > numberOfChunks) {
            return "invalid_uploader_request1";
        }

        if (this.maxFileSize != null && totalSize > this.maxFileSize) {
            return "invalid_uploader_request2";
        }

        if (fileSize != null) {
            if (chunkNumber < numberOfChunks && fileSize != chunkSize) {
                return "invalid_uploader_request3";
            }
            if (numberOfChunks > 1 && chunkNumber == numberOfChunks
                    && fileSize != ((totalSize % chunkSize) + chunkSize)) {
                return "invalid_uploader_request4";
            }
            if (numberOfChunks == 1 && fileSize != totalSize) {
                return "invalid_uploader_request5";
            }
        }

        return "valid";
    }

    public int getParamInt(HttpServletRequest req, String key, int def) {
        String value = req.getParameter(key);
        try {
            return Integer.parseInt(value);
        } catch (Exception e) {
        }
        return def;
    }

    public String getParamString(HttpServletRequest req, String key, String def) {
        String value = req.getParameter(key);
        try {
            return value == null ? def : value;
        } catch (Exception e) {
        }
        return def;
    }

    public void get(HttpServletRequest req, UploadListener listener) {
        int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
        int chunkSize = this.getParamInt(req, "chunkSize", 0);
        int totalSize = this.getParamInt(req, "totalSize", 0);
        String identifier = this.getParamString(req, "identifier", "");
        String filename = this.getParamString(req, "filename", "");
        if (validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename, null).equals("valid")) {
            String chunkFilename = getChunkFilename(chunkNumber, identifier);
            if (new File(chunkFilename).exists()) {
                listener.callback("found", chunkFilename, filename, identifier, null);
            } else {
                listener.callback("not_found", null, null, null, null);
            }

        } else {
            listener.callback("not_found", null, null, null, null);
        }
    }

    public void post(HttpServletRequest req, UploadListener listener) throws IllegalStateException, IOException {

        int chunkNumber = this.getParamInt(req, "chunkNumber", 0);
        int chunkSize = this.getParamInt(req, "chunkSize", 0);
        int totalSize = this.getParamInt(req, "totalSize", 0);
        String identifier = this.getParamString(req, "identifier", "");
        String filename = this.getParamString(req, "filename", "");

        CommonsMultipartResolver multipartResolver = new CommonsMultipartResolver(req.getSession().getServletContext());

        if (multipartResolver.isMultipart(req)) {
            // 將request變成多部分request
            MultipartHttpServletRequest multiRequest = (MultipartHttpServletRequest) req;
            // 獲取multiRequest 中所有的文件名
            Iterator<String> iter = multiRequest.getFileNames();
            while (iter.hasNext()) {
                String name = iter.next().toString();
                // if (!this.fileParameterName.equals(name)) {
                // continue;
                // }
                MultipartFile file = multiRequest.getFile(name);

                if (file != null && file.getSize() > 0) {
                    String original_filename = file.getOriginalFilename();
                    // String original_filename =
                    // files[this.fileParameterName]['originalFilename'];
                    String validation = validateRequest(chunkNumber, chunkSize, totalSize, identifier, filename,
                            (int) file.getSize());

                    if ("valid".equals(validation)) {
                        String chunkFilename = getChunkFilename(chunkNumber, identifier);

                        File f = new File(chunkFilename);
                        if (!f.exists()) {
                            file.transferTo(f);
                        }

                        int currentTestChunk = 1;
                        int numberOfChunks = (int) Math.max(Math.floor(totalSize / (chunkSize * 1.0)), 1);

                        currentTestChunk = this.testChunkExists(currentTestChunk, chunkNumber, numberOfChunks,
                                chunkFilename, original_filename, identifier, listener,"file");

                    } else {
                        listener.callback(validation, filename, original_filename, identifier,"file");
                    }
                } else {
                    listener.callback("invalid_uploader_request", null, null, null, null);
                }
            }
        }
    }

    private void pipeChunk(int number, String identifier, UploadOptions options, OutputStream writableStream)
            throws IOException {
        String chunkFilename = getChunkFilename(number, identifier);
        if (new File(chunkFilename).exists()) {
            FileInputStream inputStream = new FileInputStream(new File(chunkFilename));
            int maxlen = 1024;
            int len = 0;
            try {
                byte[] buff = new byte[maxlen];
                while ((len = inputStream.read(buff, 0, maxlen)) > 0) {
                    writableStream.write(buff, 0, len);
                }
            } finally {
                inputStream.close();
            }
            pipeChunk(number + 1, identifier, options, writableStream);
        } else {
            if (options.end)
                writableStream.close();
            if (options.listener != null)
                options.listener.onDone();
        }
    }

    public void write(String identifier, OutputStream writableStream, UploadOptions options) throws IOException {
        if (options == null) {
            options = new UploadOptions();
        }
        if (options.end == null) {
            options.end = true;
        }
        pipeChunk(1, identifier, options, writableStream);
    }

    /**
     * 
     * @param currentTestChunk
     * @param chunkNumber       當(dāng)前上傳塊
     * @param numberOfChunks    總塊數(shù)
     * @param filename          文件名稱
     * @param original_filename 源文件名稱
     * @param identifier        文件
     * @param listener          監(jiān)聽(tīng)
     * @param fileType
     * @return
     */
    private int testChunkExists(int currentTestChunk, int chunkNumber, int numberOfChunks, String filename,
            String original_filename, String identifier, UploadListener listener, String fileType) {
        String cfile = getChunkFilename(currentTestChunk, identifier);
        if (new File(cfile).exists()) {
            currentTestChunk++;
            if (currentTestChunk >= chunkNumber) {
                if (chunkNumber == numberOfChunks) {
                    try {
                        System.out.println("done");
                        // 文件合并
                        UploadOptions options = new UploadOptions();
                        File f = new File(this.temporaryFolder,
                                identifier + File.pathSeparator + FilenameUtils.getExtension(original_filename));
                        options.listener = new UploadDoneListener() {
                            @Override
                            public void onError(Exception err) {
                                listener.callback("invalid_uploader_request", f.getAbsolutePath(), original_filename,
                                        identifier, fileType);
                                clean(identifier, null);
                            }

                            @Override
                            public void onDone() {
                                listener.callback("done", f.getAbsolutePath(), original_filename, identifier, fileType);
                                clean(identifier, null);
                            }
                        };
                        this.write(identifier, new FileOutputStream(f), options);
                    } catch (FileNotFoundException e) {
                        e.printStackTrace();
                        listener.callback("invalid_uploader_request", filename, original_filename, identifier,
                                fileType);
                    } catch (IOException e) {
                        e.printStackTrace();
                        listener.callback("invalid_uploader_request", filename, original_filename, identifier,
                                fileType);
                    }
                } else {
                    listener.callback("partly_done", filename, original_filename, identifier, fileType);
                }
            } else {
                return testChunkExists(currentTestChunk, chunkNumber, numberOfChunks, filename, original_filename,
                        identifier, listener, fileType);
            }
        } else {
            listener.callback("partly_done", filename, original_filename, identifier, fileType);
        }
        return currentTestChunk;
    }

    public void clean(String identifier, UploadOptions options) {
        if (options == null) {
            options = new UploadOptions();
        }
        pipeChunkRm(1, identifier, options);
    }

    private void pipeChunkRm(int number, String identifier, UploadOptions options) {

        String chunkFilename = getChunkFilename(number, identifier);
        File file = new File(chunkFilename);
        if (file.exists()) {
            try {
                file.delete();
            } catch (Exception e) {
                if (options.listener != null) {
                    options.listener.onError(e);
                }
            }
            pipeChunkRm(number + 1, identifier, options);

        } else {
            if (options.listener != null)
                options.listener.onDone();
        }
    }

    public static interface UploadListener {
        public void callback(String status, String filename, String original_filename, String identifier,
                String fileType);
    }

    public static interface UploadDoneListener {
        public void onDone();

        public void onError(Exception err);
    }

    public static class UploadOptions {
        public Boolean end;
        public UploadDoneListener listener;
    }
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 朋友們都說(shuō)不太了解我,這個(gè)了解是比較深入的了解。無(wú)話不談,暢所欲言。 他們對(duì)我,可以做到無(wú)話不說(shuō),我可以對(duì)他們做到...
    微涼的秋閱讀 214評(píng)論 0 0
  • 一 青春,就像是小時(shí)候額外獲得的一盒糖果,不忍心吃掉,于是一直把它捧在手上,卻不曾想過(guò)有一天它自己會(huì)隨著時(shí)間的流逝...
    涵仔梅仔閱讀 857評(píng)論 2 2

友情鏈接更多精彩內(nèi)容