uniapp中APP上傳文件

uniapp提供了uni.chooseImage(選擇圖片),?uni.chooseVideo(選擇視頻)這兩個api,但是對于打包成APP的話就沒有上傳文件的api了。因此我采用了plus.android中的方式來打開手機的文件管理從而上傳文件。

下面是我封裝的APP端選擇上傳圖片,視頻,文件的一個上傳組件。

<template><view class="upload-container"><u-icon name="plus" @click="showActionSheet" size="23"></u-icon><!-- <view class="upload-list"><view class="upload-item" v-for="(item, index) in fileList" :key="index"><image v-if="item.type === 'image'" class="preview-image" :src="item.url" mode="aspectFill"@click="previewFile(item)"></image><view v-else-if="item.type === 'video'" class="preview-video" @click="previewFile(item)"><image class="video-cover" :src="item.cover || item.url" mode="aspectFill"></image><view class="video-icon"><text class="iconfont icon-play"></text></view></view><view v-else class="preview-file" @click="previewFile(item)"><text class="iconfont icon-file"></text><text class="file-name">{{ item.name }}</text></view><text class="delete-icon" @click.stop="deleteFile(index)">×</text></view><view class="upload-button" v-if="fileList.length < maxCount"@click="showActionSheet"><text class="iconfont icon-add"></text><text class="upload-text">上傳{{ getUploadTypeText() }}</text></view></view> --><!-- <view class="upload-tips" v-if="tips">{{ tips }}</view> --></view>
</template><script>
export default {name: 'FileUploader',props: {// 上傳文件類型:all-所有類型, image-圖片, video-視頻, file-文件uploadType: {type: String,default: 'all'},// 標題title: {type: String,default: '文件上傳'},// 提示文字tips: {type: String,default: '支持jpg、png、mp4、doc、pdf等格式'},// 最大上傳數量maxCount: {type: Number,default: 9},// 初始文件列表value: {type: Array,default: () => []},// 圖片類型限制imageType: {type: Array,default: () => ['jpg', 'jpeg', 'png', 'gif']},// 視頻類型限制videoType: {type: Array,default: () => ['mp4', 'mov', 'avi']},// 文件類型限制fileType: {type: Array,default: () => ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf', 'txt']},// 是否上傳到服務器uploadToServer: {type: Boolean,default: true},// 上傳接口地址uploadUrl: {type: String,default: ''}},data() {return {fileList: [],arrFile:[]}},created() {// 初始化文件列表if (this.value && this.value.length) {this.fileList = JSON.parse(JSON.stringify(this.value))}},watch: {value: {handler(newVal) {if (newVal && newVal.length) {this.fileList = JSON.parse(JSON.stringify(newVal))}},deep: true}},methods: {// 獲取上傳類型文本getUploadTypeText() {switch (this.uploadType) {case 'image':return '圖片'case 'video':return '視頻'case 'file':return '文件'default:return '文件'}},// 顯示操作菜單showActionSheet() {let itemList = []// if (this.uploadType === 'all' || this.uploadType === 'image') {//   itemList.push('上傳圖片')// }// if (this.uploadType === 'all' || this.uploadType === 'video') {//   itemList.push('上傳視頻')// }if (this.uploadType === 'all' || this.uploadType === 'file') {// 檢查平臺支持// #ifdef APP-PLUSconst appPlus = plus.os.name.toLowerCase();if (appPlus === 'android' || appPlus === 'ios') {itemList.push('上傳文件')}// #endif// #ifdef H5itemList.push('上傳文件')// #endif// #ifdef MP-WEIXINitemList.push('上傳文件')// #endif}uni.showActionSheet({itemList,success: res => {const index = res.tapIndexif (itemList[index] === '上傳圖片') {this.chooseImage()} else if (itemList[index] === '上傳視頻') {this.chooseVideo()} else if (itemList[index] === '上傳文件') {this.selectAndUploadFile()}}})},// 選擇圖片chooseImage() {uni.chooseImage({count: this.maxCount - this.fileList.length,sizeType: ['original', 'compressed'],sourceType: ['album', 'camera'],success: res => {const tempFiles = res.tempFiles// 檢查文件類型for (let i = 0; i < tempFiles.length; i++) {const file = tempFiles[i]const extension = this.getFileExtension(file.path)if (!this.imageType.includes(extension.toLowerCase())) {uni.showToast({title: `不支持${extension}格式的圖片`,icon: 'none'})continue}// 添加到文件列表const fileItem = {name: this.getFileName(file.path),url: file.path,size: file.size,type: 'image',extension: extension,status: 'ready' // ready, uploading, success, fail}this.fileList.push(fileItem)// 上傳到服務器if (this.uploadToServer) {this.uploadFileToServer(fileItem, this.fileList.length - 1)}}this.emitChange()}})},// 選擇視頻chooseVideo() {uni.chooseVideo({count: 1,sourceType: ['album', 'camera'],success: res => {const extension = this.getFileExtension(res.tempFilePath)if (!this.videoType.includes(extension.toLowerCase())) {uni.showToast({title: `不支持${extension}格式的視頻`,icon: 'none'})return}// 添加到文件列表const fileItem = {name: this.getFileName(res.tempFilePath),url: res.tempFilePath,cover: '', // 視頻封面,可以通過后端生成size: res.size,duration: res.duration,type: 'video',extension: extension,status: 'ready'}this.fileList.push(fileItem)// 上傳到服務器if (this.uploadToServer) {this.uploadFileToServer(fileItem, this.fileList.length - 1)}this.emitChange()}})},// 選擇并上傳文件函數selectAndUploadFile(){// 顯示加載提示uni.showLoading({title: '準備選擇文件',});// 選擇文件this.chooseFile().then(filePath => {// 選擇文件成功后上傳return this.uploadFile(filePath);}).then(result => {// 上傳成功的處理uni.hideLoading();uni.showToast({title: '上傳成功',icon: 'success'});console.log('上傳結果:', result);// 在此處理上傳成功后的業務邏輯}).catch(error => {// 錯誤處理uni.hideLoading();uni.showToast({title: error.message || '操作失敗',icon: 'none'});console.error('文件操作錯誤:', error);});},// 選擇文件方法chooseFile(){return new Promise((resolve, reject) => {try {// #ifdef APP-PLUSconst MediaStore = plus.android.importClass('android.provider.MediaStore');const main = plus.android.runtimeMainActivity();const Uri = plus.android.importClass('android.net.Uri');plus.io.chooseFile({title: '選擇文件',filetypes: ['xlsx', 'xls', 'pdf', 'doc', 'docx'], // 允許的文件類型multiple: false, // 是否允許多選}, (event) => {if (event.files && event.files.length > 0) {const tempFilePath = decodeURIComponent(event.files[0]);console.log('選擇的虛擬路徑:', tempFilePath);// 解析文件IDconst uri = MediaStore.Files.getContentUri("external");// 導入contentResolverplus.android.importClass(main.getContentResolver());// 從虛擬路徑中提取IDconst parts = tempFilePath.split(':');const fileId = parts[parts.length - 1];console.log('文件ID:', fileId);// 查詢真實路徑let cursor = main.getContentResolver().query(uri, ['_data'], "_id=?", [fileId], null);plus.android.importClass(cursor);let realPath = null;if (cursor != null && cursor.moveToFirst()) {const columnIndex = cursor.getColumnIndexOrThrow('_data');realPath = cursor.getString(columnIndex);cursor.close();}if (realPath) {// 轉換為file://格式const filePath = 'file://' + realPath;console.log('文件真實路徑:', filePath);resolve(filePath);} else {reject(new Error('無法獲取文件路徑'));}} else {reject(new Error('未選擇文件'));}}, (error) => {reject(new Error('選擇文件失敗: ' + error.message));});// #endif// #ifdef H5// H5環境下的文件選擇const input = document.createElement('input');input.type = 'file';input.accept = '.xlsx,.xls,.pdf,.doc,.docx';input.onchange = (e) => {const file = e.target.files[0];if (file) {resolve(file);} else {reject(new Error('未選擇文件'));}};input.click();// #endif// #ifdef MP-WEIXIN// 微信小程序環境wx.chooseMessageFile({count: 1,type: 'file',extension: ['xlsx', 'xls', 'pdf', 'doc', 'docx'],success: (res) => {if (res.tempFiles && res.tempFiles.length > 0) {resolve(res.tempFiles[0].path);} else {reject(new Error('未選擇文件'));}},fail: (err) => {reject(new Error('選擇文件失敗: ' + err.errMsg));}});// #endif// 其他平臺// #ifdef MP-ALIPAY || MP-BAIDU || MP-TOUTIAO || MP-QQreject(new Error('當前平臺不支持文件選擇'));// #endif} catch (e) {reject(new Error('選擇文件出錯: ' + e.message));}});},// 上傳文件方法uploadFile (filePath){return new Promise((resolve, reject) => {uni.showLoading({title: '上傳中...',mask: true});const token = uni.getStorageSync('token');if (!token) {reject(new Error('登錄狀態已失效,請重新登錄'));return;}// 準備表單數據const formData = {// 這里可以添加業務所需參數shangpinbianma: this.goodsDetail?.bianma || '',uploadTime: new Date().getTime()};console.log(filePath,'filepath')uni.uploadFile({url: '...', // 替換為您的服務器地址method: 'POST',name: 'file',filePath: filePath,formData: formData,header: {accept: "application/json",Authorization:"",},success: (res) => {console.log('上傳響應:', res);// 檢查登錄狀態if (res.statusCode === 200) {let result;try {// 解析返回結果if (typeof res.data === 'string') {result = JSON.parse(res.data);} else {result = res.data;}// 檢查登錄狀態if (result.code === '-110' || result.code === '-120' || result.code === '-130' || result.code === '-150') {console.log('登錄已失效');if (result.code !== '-120') {uni.showToast({title: '登錄已失效',icon: 'none'});}// 跳轉到登錄頁uni.reLaunch({url: '/pages/login/index'});reject(new Error('登錄已失效'));} else if (result.success) {//此處記得修改成為你的響應判斷// 上傳成功resolve(result);} else {// 其他業務錯誤reject(new Error(result.message || '上傳失敗'));}} catch (e) {reject(new Error('解析上傳結果失敗: ' + e.message));}} else {reject(new Error('上傳請求失敗,狀態碼: ' + res.statusCode));}},fail: (err) => {reject(new Error('上傳失敗: ' + (err.errMsg || JSON.stringify(err))));},complete: () => {uni.hideLoading();}});});},// 獲取文件擴展名getFileExtension(path) {if (!path) return ''return path.substring(path.lastIndexOf('.') + 1) || ''},// 獲取文件名getFileName(path) {if (!path) return ''return path.substring(path.lastIndexOf('/') + 1) || '未命名文件'},// 觸發變更事件emitChange() {this.$emit('input', this.fileList)this.$emit('change', this.fileList)}}
}
</script><style lang="scss">
.upload-container {width: 100%;padding: 20rpx;box-sizing: border-box;.upload-title {font-size: 30rpx;font-weight: bold;margin-bottom: 20rpx;}.upload-list {display: flex;flex-wrap: wrap;}.upload-item, .upload-button {position: relative;width: 200rpx;height: 200rpx;margin: 0 20rpx 20rpx 0;border-radius: 8rpx;overflow: hidden;box-sizing: border-box;}.upload-item {border: 1rpx solid #eee;.preview-image {width: 100%;height: 100%;}.preview-video {width: 100%;height: 100%;position: relative;.video-cover {width: 100%;height: 100%;}.video-icon {position: absolute;left: 50%;top: 50%;transform: translate(-50%, -50%);width: 60rpx;height: 60rpx;background-color: rgba(0, 0, 0, 0.5);border-radius: 50%;display: flex;align-items: center;justify-content: center;.icon-play {color: #fff;font-size: 30rpx;}}}.preview-file {width: 100%;height: 100%;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #f7f7f7;.icon-file {font-size: 60rpx;color: #999;margin-bottom: 10rpx;}.file-name {font-size: 24rpx;color: #666;width: 90%;text-align: center;white-space: nowrap;overflow: hidden;text-overflow: ellipsis;}}.delete-icon {position: absolute;right: 0;top: 0;width: 40rpx;height: 40rpx;background-color: rgba(0, 0, 0, 0.5);color: #fff;font-size: 30rpx;display: flex;align-items: center;justify-content: center;z-index: 10;}}.upload-button {border: 1rpx dashed #ddd;display: flex;flex-direction: column;align-items: center;justify-content: center;background-color: #f7f7f7;.icon-add {font-size: 60rpx;color: #999;margin-bottom: 10rpx;}.upload-text {font-size: 24rpx;color: #999;}}.upload-tips {font-size: 24rpx;color: #999;margin-top: 10rpx;}
}
</style>

改組件可以直接調用,希望可以幫助到大家。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/74611.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/74611.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/74611.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

推陳換新系列————java8新特性(編程語言的文藝復興)

文章目錄 前言一、新特性秘籍二、Lambda表達式2.1 語法2.2 函數式接口2.3 內置函數式接口2.4 方法引用和構造器引用 三、Stream API3.1 基本概念3.2 實戰3.3 優勢 四、新的日期時間API4.1 核心概念與設計原則4.2 核心類詳解4.2.1 LocalDate&#xff08;本地日期&#xff09;4.2…

樹莓派5從零開發至脫機腳本運行教程——1.系統部署篇

樹莓派5應用實例——工創視覺 前言 哈嘍&#xff0c;各位小伙伴&#xff0c;大家好。最近接觸了樹莓派&#xff0c;然后簡單的應用了一下&#xff0c;學習程度并不是很深&#xff0c;不過足夠剛入手樹莓派5的小伙伴們了解了解。后面的幾篇更新的文章都是關于開發樹莓派5的內容…

GPT Researcher 的win docker安裝攻略

github網址是&#xff1a;https://github.com/assafelovic/gpt-researcher 因為docker安裝方法不夠清晰&#xff0c;因此寫一個使用方法 以下是針對 Windows 系統 使用 Docker 運行 AI-Researcher 項目的 詳細分步指南&#xff1a; 步驟 1&#xff1a;安裝 Docker 下載 Docke…

【后端】【Django DRF】從零實現RBAC 權限管理系統

Django DRF 實現 RBAC 權限管理系統 在 Web 應用中&#xff0c;權限管理 是一個核心功能&#xff0c;尤其是在多用戶系統中&#xff0c;需要精細化控制不同用戶的訪問權限。本文介紹如何使用 Django DRF 設計并實現 RBAC&#xff08;基于角色的訪問控制&#xff09;系統&…

C#基礎學習(五)函數中的ref和out

1. 引言&#xff1a;為什么需要ref和out&#xff1f; ?問題背景&#xff1a;函數參數默認按值傳遞&#xff0c;值類型在函數內修改不影響外部變量&#xff1b;引用類型重新賦值時外部對象不變。?核心作用&#xff1a;允許函數內部修改外部變量的值&#xff0c;實現“雙向傳參…

八綱辨證總則

一、八綱辨證的核心定義 八綱即陰、陽、表、里、寒、熱、虛、實&#xff0c;是中醫分析疾病共性的綱領性辨證方法。 作用&#xff1a;通過八類證候歸納疾病本質&#xff0c;為所有辨證方法&#xff08;如臟腑辨證、六經辨證&#xff09;的基礎。 二、八綱分類與對應關系 1. 總…

【linux重設gitee賬號密碼 克隆私有倉庫報錯】

出現問題時 Cloning into xxx... remote: [session-1f4b16a4] Unauthorized fatal: Authentication failed for https://gitee.com/xxx/xxx.git/解決方案 先打開~/.git-credentials vim ~/.git-credentials或者創建一個 torch ~/.git-credentials 添加授權信息 username/pa…

綠聯NAS安裝內網穿透實現無公網IP也能用手機平板遠程訪問經驗分享

文章目錄 前言1. 開啟ssh服務2. ssh連接3. 安裝cpolar內網穿透4. 配置綠聯NAS公網地址 前言 大家好&#xff0c;今天給大家帶來一個超級炫酷的技能——如何在綠聯NAS上快速安裝cpolar內網穿透工具。想象一下&#xff0c;即使沒有公網IP&#xff0c;你也能隨時隨地遠程訪問自己…

CSS 美化頁面(一)

一、CSS概念 CSS&#xff08;Cascading Style Sheets&#xff0c;層疊樣式表&#xff09;是一種用于描述 HTML 或 XML&#xff08;如 SVG、XHTML&#xff09;文檔 樣式 的樣式表語言。它控制網頁的 外觀和布局&#xff0c;包括字體、顏色、間距、背景、動畫等視覺效果。 二、CS…

空轉 | GetAssayData doesn‘t work for multiple layers in v5 assay.

問題分析 當我分析多個樣本的時候&#xff0c;而我的seurat又是v5時&#xff0c;通常就會出現這樣的報錯。 錯誤的原因有兩個&#xff1a; 一個是參數名有slot變成layer 一個是GetAssayData 不是自動合并多個layers&#xff0c;而是選擇保留。 那么如果我們想合并多個樣本&…

UE4學習筆記 FPS游戲制作17 讓機器人持槍 銷毀機器人時也銷毀機器人的槍 讓機器人射擊

添加武器插槽 打開機器人的Idle動畫&#xff0c;方便查看武器位置 在動畫面板里打開骨骼樹&#xff0c;找到右手的武器節點&#xff0c;右鍵添加一個插槽&#xff0c;重命名為RightWeapon&#xff0c;右鍵插槽&#xff0c;添加一個預覽資產&#xff0c;選擇Rifle&#xff0c;根…

【JavaScript】七、函數

文章目錄 1、函數的聲明與調用2、形參默認值3、函數的返回值4、變量的作用域5、變量的訪問原則6、匿名函數6.1 函數表達式6.2 立即執行函數 7、練習8、邏輯中斷9、轉為布爾型 1、函數的聲明與調用 function 函數名(形參列表) {函數體 }eg&#xff1a; // 聲明 function sayHi…

硬件基礎--05_電壓

電壓(電勢差) 有了電壓&#xff0c;電子才能持續且定向移動起來&#xff0c;所有電壓是形成電流的必要條件。 電壓越大&#xff0c;能“定向移動”起來的電子就越多&#xff0c;電流就會越大。 有電壓的同時&#xff0c;形成閉合回路才會有電流&#xff0c;不是有電壓就有電流…

ES數據過多,索引拆分

公司企微聊天數據存儲在 ES 中&#xff0c;雖然按照企業分儲在不同的ES 索引中&#xff0c;但某些常用的企微主體使用量還是很大。4年中一個索引存儲數據已經達到46多億條數據&#xff0c;占用存儲3.1tb, ES 配置 由于多一個副本&#xff0c;存儲得翻倍&#xff0c;成本考慮…

存儲服務器是指什么

今天小編主要來為大家介紹存儲服務器主要是指什么&#xff0c;存儲服務器與傳統的物理服務器和云服務器是不同的&#xff0c;其是為了特定的目標所設計的&#xff0c;在硬件配置方式上也有著一定的區別&#xff0c;存儲空間會根據需求的不同而改變。 存儲服務器中一般會配備大容…

golang不使用鎖的情況下,對slice執行并發寫操作,是否會有并發問題呢?

背景 并發問題最簡單的解決方案加個鎖,但是,加鎖就會有資源爭用,提高并發能力其中的一個優化方向就是減少鎖的使用。 我在之前的這篇文章《開啟多個協程,并行對struct中的每個元素操作,是否會引起并發問題?》中討論過多協程場景下struct的并發問題。 Go語言中的slice在…

Java知識整理round1

一、常見集合篇 1. 為什么數組索引從0開始呢&#xff1f;假如從1開始不行咩 數組&#xff08;Array&#xff09;&#xff1a;一種用連續的內存空間存儲相同數據類型數據的線性數據結構 &#xff08;1&#xff09;在根據數組索引獲取元素的時候&#xff0c;會用索引和尋址公式…

【C++指針】搭建起程序與內存深度交互的橋梁(下)

&#x1f525;&#x1f525; 個人主頁 點擊&#x1f525;&#x1f525; 每文一詩 &#x1f4aa;&#x1f3fc; 往者不可諫&#xff0c;來者猶可追——《論語微子篇》 譯文&#xff1a;過去的事情已經無法挽回&#xff0c;未來的歲月還可以迎頭趕上。 目錄 C內存模型 new與…

JavaScript創建對象的多種方式

在JavaScript中&#xff0c;創建對象有多種方式&#xff0c;每種方式都有其優缺點。本文將介紹四種常見的對象創建模式&#xff1a;工廠模式、構造函數模式、原型模式和組合模式&#xff0c;并分析它們的特點以及如何優化。 1. 工廠模式 工廠模式是一種簡單的對象創建方式&am…

muduo庫的思路梳理

前言 對于muduo庫源碼的剖析我發現還是有些混亂的&#xff0c;所以這里再次梳理一下muduo網絡庫爭取可以簡單明了 首先對于muduo庫來說&#xff0c;不能想的得太過于復雜&#xff0c;它無非就是一個線程池加上epoll組成的網絡庫 這里我們從用的角度出發理解muoduo網絡庫 #inc…