簡單實作 Vue 的拖拉檔案上傳並透過 Ajax 呼叫 Java 後端 API 來處理上傳的檔案內容,後端的部份因為 Server 問題,僅留後端程式碼、注意事項和截圖結果呈現。

Demo 以及 CodePen 部分皆只呈現前端頁面的部份並使用 CDN 的方式引入,而 CodePen 有使用 Bootstrap 的按鈕, 上傳 icon 則是 FontAwesome,有需要請自行引入!

以下為前端頁面拖拉檔案或點擊上傳的部份,上傳後可以分析出檔案的一些資訊,上傳完後也會出現刪除檔案的按鈕。而範例的 code 會加上呼叫後端 API 的段落並利用 axios 來呼叫。


前端 Demo(不包含呼叫後端 api 但範例 code 會貼上):


Drop file or click to upload
extension support: txt
maximum file size: 5 MB
fileName:
fileZise(bytes):
extension:

前往 CodePen 觀看


CSS:


.dropZone {
    height: 200px;
    position: relative;
    border: 2px dashed #eee;
}

.dropZone:hover {
    border: 2px solid #2e94c4;
}

.dropZone:hover .dropZone-title {
    color: #1975A0;
}

.dropZone-info {
    color: #A8A8A8;
    position: absolute;
    top: 50%;
    width: 100%;
    transform: translate(0, -50%);
    text-align: center;
}

.dropZone-title {
    color: #787878;
}

.dropZone input {
    position: absolute;
    cursor: pointer;
    top: 0px;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
}

.dropZone-upload-limit-info {
    display: flex;
    justify-content: flex-start;
    flex-direction: column;
}

.dropZone-over {
    background: #5C5C5C !important;
    opacity: 0.8;
    border: 2px solid #2e94c4;
}

.dropZone-uploaded {
    height: 200px;
    position: relative;
    border: 2px dashed #eee;
}

.dropZone-uploaded-info {
    display: flex;
    flex-direction: column;
    align-items: center;
    color: #A8A8A8;
    position: absolute;
    top: 50%;
    width: 100%;
    transform: translate(0, -50%);
    text-align: center;
}

.removeFile {
    width: 200px;
}

.btn-primary {
    color: #fff;
    background-color: #1170C8;
    border-radius: 5px;
}

HTML:


<div id="app">
    <div v-if="!file">
        <div :class="['dropZone', dragging ? 'dropZone-over' : '']" @dragestart="dragging = true" @dragenter="dragging = true" @dragleave="dragging = false">
            <div class="dropZone-info" @drag="onChange">
                <!-- font-awesome upload icon -->
                <span class="fa fa-cloud-upload dropZone-title"></span>
                <span class="dropZone-title">Drop file or click to upload</span>
                <div class="dropZone-upload-limit-info">
                    <div>extension support: txt</div>
                    <div>maximum file size: 5 MB</div>
                </div>
            </div>
            <input type="file" @change="onChange">
        </div>
    </div>
    <div v-else class="dropZone-uploaded">
        <div class="dropZone-uploaded-info">
            <span class="dropZone-title">Uploaded</span>
            <button type="button" class="btn btn-primary removeFile" @click="removeFile">Remove File</button>
        </div>
    </div>
    <div class="uploadedFile-info">
        <div>fileName: {{ file.name }}</div>
        <div>fileZise(bytes): {{ file.size }}</div>
        <div>extension:{{ extension }}</div>
    </div>
</div>

JavaScript:


<script src="https://unpkg.com/vue@2.5.22/dist/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>

var app = new Vue({
    el: 'app',
    data() {
        return {
            file: '',
            dragging: false
        }
    },
    methods: {
        onChange(e) {
            // 判斷拖拉上傳或點擊上傳的 event
            var files = e.target.files || e.dataTransfer.files;

            // 預防檔案為空檔
            if (!files.length) {
                this.dragging = false;
                return;
            }

            this.createFile(files[0]);
        },
        createFile(file) {
            // 附檔名判斷
            if (!file.type.match('text.*')) {
                alert('please select txt file');
                this.dragging = false;
                return;
            }

            // 檔案大小判斷
            if (file.size > 5000000) {
                alert('please check file size no more than 5 MB')
                this.dragging = false;
                return;
            }

            this.file = file;
            this.dragging = false;

            // axios post 的 url 記得更換你自己的 api url
            let json = new FormData();
            json.append('uploadFile', this.file)

            axios.post('http://localhost:8080/uploadFile', json
                {
                    headers: {
                        'Content-Type': 'multipart/form-data'
                    }
                })
                .then(res => {
                    if (res.data !== null && res.data.length > 1) {
                        console.log(res.data)
                    }
                })
        },
        removeFile() {
            this.file = '';
        }
    },
    computed: {
        // 前端擷取附檔名
        extension() {
            return (this.file) ? this.file.name.split('.').pop() : '';
        }
    }
});


後端 Java 部分(以 SrpingMVC 為例子):


configuration multipartResolver(我的環境是在 applicationContext-webapp.xml 底下):


如果沒有設定會噴 error log!


<!-- for <form ... enctype="multipart/form-data" /> -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
    <!-- The maximum file size in bytes -->
    <property name="maxUploadSize" value="5000000" />
</bean>

controller:


...
// your file process service
@Autowired FileSerivce fileService;
...
@PostMapping("uploadFile")
@ResponseBody
public String searchSimilarDocumentByDocument(@RequestParam("uploadFile") MultipartFile file) {
    return new fileService.txtToString(file);
}
...

service:


...
// 不過這個做成 utils 可能比較適合
public static String txtToString(MultipartFile file) throws IOException {
    return new String(file.getBytes(), "UTF-8").trim();
}
...

Test Result:


input: test.txt

context: 測試文件

log result:

Note:

code 都是簡單的範例應用,礙於 server 緣故,後端的實作基本上就是貼重要的 code 和擷取結果,之後前端利用 axios 接收到剛剛的結果在看要怎麼去處理像是輸出的某些地方之類的。而真實使用的時候一些例外處理還有副檔名前後端處理,最後前端要取的什麼訊息或錯誤訊息都得記得補上哦。