Android+Spring Boot 選擇+上傳+下載文件


2021.02.03更新

1 概述

前端Android,上傳與下載文件,使用OkHttp處理請求,后端使用Spring Boot,處理Android發送來的上傳與下載請求。這個其實不難,就是特別多奇奇怪怪的坑,因此,就一句話, 希望各位讀者能少走彎路。

2 環境

  • Win10
  • Spring Boot 2.2.2
  • IDEA 2019.3.1
  • Android Studio 3.6
  • Tomcat 9.0.30

3 Android

3.1 准備工作

3.1.1 新建工程

這次用一個全新的例子寫博客,因此從新建工程開始:

在這里插入圖片描述

在這里插入圖片描述

3.1.2 AndroidManifest.xml

加入

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application android:usesCleartextTraffic="true">

主要是各種權限申請:

  • 網絡權限
  • 讀寫SD卡權限
  • HTTP請求的權限

3.1.3 build.gradle

加入

compileOptions {
	sourceCompatibility = 1.8
    targetCompatibility = 1.8
}

這個是支持JDK8的。

還有這兩個OkHttpConscrypt依賴,最新版本戳這里這里查看。

implementation 'com.squareup.okhttp3:okhttp:4.9.0'
implementation 'org.conscrypt:conscrypt-android:2.5.1'

3.1.4 上傳文件

手動上傳一些文件到AVD中,為下一步選擇與上傳文件做准備,先把這個窗口工具欄打開:

在這里插入圖片描述

打開后,點擊在右側欄中的Device File Explorer

在這里插入圖片描述

然后選擇sdcard文件夾上傳文件即可,其他文件夾一般沒有權限:

在這里插入圖片描述

3.1.5 布局

組件如下:

  • 三個button:上傳/下載/選擇文件
  • 一個EditText:上傳文件名與下載文件名
  • 一個ImageView:顯示下載的圖片

在這里插入圖片描述

3.2 選擇文件

3.2.1 申請權限

首先申請動態讀寫文件權限(其實選擇文件只需要讀權限,因為后面的下載需要寫權限所以這里就一起申請了):

在這里插入圖片描述

使用checkSelfPermission檢查權限,參數為一個Context+StringString表示相應的權限:

  • 如果有權限就會返回PackageManager.PERMISSION_GRANTED
  • 沒有就返回PackageManager.PERMISSION_DENIED

沒有就利用requestPermissions()申請,參數為Content+String[]+intString[]表示要申請的所有權限,int是一個requestCode

3.2.2 Intent選擇文件

在這里插入圖片描述

新建一個Intent后,設置選擇類型,然后就重寫onActivityResult

在這里插入圖片描述

這是簡化了的處理,因為選擇的是圖片,選擇其他文件的話可以參照這里

其中path是選擇的文件的路徑,讀者可能有疑問下面的路徑是怎么拼接的:

String path = dir.toString().substring(0,dir.toString().indexOf("0")+2) +
	DocumentsContract.getDocumentId(uri).split(":")[1];

其實是拼湊過來的,因為這是圖片,是下面版本的簡化版:

在這里插入圖片描述

3.3 上傳文件

在這里插入圖片描述

參數為文件路徑與文件名,然后使用OkHttpClient,因為是文件,用的請求體是MultipartBody,增加一個叫fileFormDataPart與一個叫filenameFormDataPart,然后使用execute()發送請求,body()獲取響應內容。

這里假設了后端響應一個布爾,表示上傳成功或失敗,url的話使用了本地的路徑,注意不能是localhost,使用內網ip,然后還要與后端對應

3.4 下載文件

在這里插入圖片描述

參數為一個文件名,根據這個文件名返回對應的文件,返回一個File。這里請求體可以選擇FormBodyMultipartBody,因為這是一個文件名參數,這里筆者為了統一就選擇了MultipartBody,使用FormBody的話,只需要將RequestBody的那一行改為:

RequestBody body = new FormBody.Builder().add("filename",filename).build();

有了請求體后發送請求獲取響應體,進而獲取輸入流,然后首先需要判斷是否為空,但不能直接這樣判斷:

inputStream == null

因為后端是這樣的:

在這里插入圖片描述

從響應體獲取的inputStream肯定不為null,需要先進行一次讀取(也就是判斷里面的文件是否為null),若為null的話刪除這個文件,不為null的話繼續讀取並寫入文件。

4 Spring Boot

4.1 准備工作

4.1.1 新建工程

在這里插入圖片描述

打包方式JAR/WAR均可:

在這里插入圖片描述

兩個,一個Spring Web+一個模板引擎,用於顯示視圖,如果不需要顯示可以不選。

在這里插入圖片描述

4.1.2 application.properties

在這里插入圖片描述

配置了三項:

  • 上傳文件總大小限制
  • 單個文件大小限制
  • 上傳路徑

4.1.3 pom.xml

這里其實不需要干什么,只是如果下載依賴慢的話,可以這樣設置settings.xml文件,在<mirrors>中加上:

<mirror>
    <id>alimaven</id>
    <name>aliyun maven</name>
    <url>http://maven.aliyun.com/nexus/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
</mirror>

<mirror>
    <id>uk</id>
    <mirrorOf>central</mirrorOf>
    <name>Human Readable Name for this Mirror.</name>
    <url>http://uk.maven.org/maven2/</url>
</mirror>

<mirror>
    <id>CN</id>
    <name>OSChina Central</name>
    <url>http://maven.oschina.net/content/groups/public/</url>
    <mirrorOf>central</mirrorOf>
</mirror>

<mirror>
    <id>nexus</id>
    <name>internal nexus repository</name>
    <!-- <url>http://192.168.1.100:8081/nexus/content/groups/public/</url>-->
    <url>http://repo.maven.apache.org/maven2</url>
    <mirrorOf>central</mirrorOf>
</mirror>

Windows用戶的話這個文件在

C:\Users\{username}\.m2\settings.xml

Linux的話在

~/.m2/settings.xml

4.2 處理上傳文件

在這里插入圖片描述

首先對應的POST映射路徑為/upload,與Android端的路徑對應,然后需要一個表示文件的MultipartFile與一個表示文件名的String,判斷這兩個是否為空。

接着如果上傳的文件夾不存在則先創建,存在的話直接進行復制,然后根據復制成功或失敗返回布爾值。復制使用了Files.copy(),第一個InputStream為上傳文件的輸入流,第二個Path為存儲文件的路徑,resolve(filename)相當於在上傳目錄下的filename文件。

4.3 處理下載文件

下載的話可以選擇使用GETPOST請求,這里選擇了POST請求,因為Android端是POST請求,需要對應。

在這里插入圖片描述

首先根據文件名獲取對應文件,判斷文件是否存在后返回一個ResponseEntity,需要設定content-typebody,content-type,根據需要設置即可。這里是圖片,默認.jpg.pngbody的話使用FileSystemResource,直接new一個放進body即可。

如果不存在相應的文件則返回null,這里需要注意一下前端的判斷,不能直接判斷ResponseBody是否為null

5 測試

5.1 Postman測試

5.1.1 上傳測試

Headers中設置了Content-Typemultipart/form-data后:

在這里插入圖片描述

body添加一個叫file的文件與一個叫filename的字符串表示文件名:

在這里插入圖片描述

發送,返回true

在這里插入圖片描述

服務器端有輸出提示:

在這里插入圖片描述

查看文件夾:

在這里插入圖片描述

5.1.2 下載測試

file參數關掉,保留filename,修改路徑:

在這里插入圖片描述

然后發送,postman可以直接顯示圖片:

在這里插入圖片描述

5.2 Android端測試

5.2.1 上傳測試

在這里插入圖片描述

后端提示:

在這里插入圖片描述

查看文件夾:

在這里插入圖片描述

5.2.2 下載測試

輸入文件名后直接下載:

在這里插入圖片描述

默認的話是放在這里,按需要更改位置即可,注意加上寫權限:

在這里插入圖片描述

若看不到文件選擇synchronize即可。

在這里插入圖片描述

6 部署到服務器

服務器用的是Tomcat,需要修改一些Spring Boot的部分。

6.1 部署

6.1.1 改變打包方式

pom.xmljar改成war

在這里插入圖片描述

6.1.2 去除Tomcat依賴

pom.xml加入:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-tomcat</artifactId>
    <scope>provided</scope>
</dependency>

6.1.3 修改Main

修改Main類,讓其繼承SpringBootServletInitializer,重寫configure(),同時main()保持不變。

修改前:

在這里插入圖片描述

修改后:

在這里插入圖片描述

6.1.4 修改路徑

這個按需要修改即可,在這里不需要,注意就是@PostMapping@GetMapping等都是相對於

tomcat/webapps/項目/

目錄下的。

6.1.5 設置打包名字

<build>加上<finalName>

在這里插入圖片描述

6.1.6 打包

在這里插入圖片描述

6.1.7 上傳到服務器

打包后的文件放在target下,使用scp上傳即可。這里是本地的Tomcat,就這接移動war了。

6.1.8 運行

開啟Tomcat,雙擊startup.bat即可:

在這里插入圖片描述

Linux的話:

cd xxxx/tomcat/bin
./startup.sh

6.2 測試

在測試前需要確保沒有占用相應端口。默認8080,也就是說,如果不改端口的話,需要關閉IDEA運行中的SpringBoot應用。

6.2.1 Postman測試

上傳測試,注意需要改路徑,加上打包項目名,ip的話可以使用localhost或者內網ip

在這里插入圖片描述

服務器這邊收到了,因為上傳路徑只是直接寫名字,因此會與startup.bat同一路徑:

在這里插入圖片描述

下載測試:

在這里插入圖片描述

服務器的輸出:

在這里插入圖片描述

6.2.2 Android端測試

Android端需要修改路徑即可,加上war打包的名字。

在這里插入圖片描述

這里打包的名字是kr,直接加上即可:

在這里插入圖片描述

上傳那里也是要加上,然后:

在這里插入圖片描述

服務器的輸出:

在這里插入圖片描述

查看文件:

在這里插入圖片描述

7 一些坑

7.1 權限

Android需要讀權限才能讀取文件並上傳,需要寫權限才能保存從服務器返回的文件,在AndroidManifest.xml中加入:

<manifest>...
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application>...</application>

這是外部設備的讀寫權限。當然,加入這個還不能訪問,因為,Android6.0以后還需要動態申請權限,所以:

String [] permission = new String[]{
	"android.permission.READ_EXTERNAL_STORAGE",
	"android.permission.WRITE_EXTERNAL_STORAGE"
};
if(
	ActivityCompat.checkSelfPermission(this,permission[0]) != PackageManager.PERMISSION_GRANTED
	||
	ActivityCompat.checkSelfPermission(this,permission[1]) != PackageManager.PERMISSION_DENIED
){
    ActivityCompat.requestPermissions(this,permission,1);
}

7.2 路徑

需要保證下面幾個路徑正確,還有可讀,可寫等:

  • URL路徑不能錯
  • 前端上傳文件的路徑
  • 后端接收前端上傳文件的路徑
  • 后端發送前端需要下載的文件的路徑
  • 前端接收下載文件的路徑

7.3 有關HTTP的問題

7.3.1 OkHttpstream關閉

在這里插入圖片描述

若前端是這樣寫的,在工具類中返回了之后Response已經關閉,因此需要讀取輸入流之類的需要先讀取再返回,而不是返回一個ResponseBodyInputStream進行讀取,否則會提示"closed"

7.3.2 HTTP

Android P開始默認禁用HTTP,因此可以使用HTTPS或者在AndroidManifest.xml中允許HTTP連接:

<application android:usesCleartextTraffic="true">

7.3.3 線程

網絡請求不能在主線程中,新開一個線程即可。

7.3.4 AVD

若檢查過了服務器與Android端沒問題,那么有可能是AVD的問題,解決方法很簡單,卸載,重啟AVD,注意一定要卸載再重啟。

7.4 ip

在本地測試的話后端可以直接localhost,在Android端不能直接localhost,可以使用ipconfigifconfig查看內網ip,輸入內網ip即可。

在這里插入圖片描述

若在服務器上測試直接使用服務器ip

7.5 判空處理

對於前端,應該判斷存儲路徑是否為空,是否為null等,再傳給后端。對於后端,要判斷文件是否存在等,不存在就返回null,這時又需要前端進行判斷返回的null,在下載文件時,雖然對不存在的文件后端返回null,但是,前端收到的是一個InputStream,不能直接判斷是否為null,需要先讀取一次,再進行剩下的讀取:

在這里插入圖片描述

8 源碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM