- 利用水墨映客作為COS伺服器
- 利用picGo配合typora上傳圖片
- 安裝PicGo(以Windows為例)
- 安裝lankong外掛
- 在SpringBoot中開發圖片上傳工具類
- 設定圖片上傳請求的引數
- 設定圖片的引數
- 讀取圖片內容並寫入請求
- 獲取響應
- 利用picGo配合typora上傳圖片
利用水墨映客作為COS伺服器
文章類網站專案的圖片儲存都是一個很重要的問題,一般都是使用雲服務廠商的COS物件儲存服務(參考前文:使用騰訊雲物件儲存搭建圖床) ,雖然小網站的需求不大,購買也不貴,但是還是想能不能找個免費的,畢竟能省就省嘛。
平常有些部落格或者筆記的都是用markdown編輯,圖片一般都是上傳圖床,但是很多圖床都沒有開放介面。最近用路過圖床的時候網站沒開啟,諮詢管理說有DNS汙染,便又搜尋有沒有好用一些的圖床,就發現了水墨映客圖床,雖然容量有限,但是他每天簽到都可以擴充容量,並且最最重要的我發現水墨映客開放上傳介面,於是就開始鼓搗想著利用水墨映客作為COS伺服器。
水墨圖床地址:圖片映客--水墨圖床,免費專業的高速外鏈圖床
分為兩種,一種還是利用picGo配合typora,另一種是在SpringBoot中開發為工具類專門用作圖片上傳。
利用picGo配合typora上傳圖片
安裝PicGo(以Windows為例)
先下載PicGo的客戶端安裝包,找到對應的平臺安裝包然後下載。
下載連結:
- GitHub:https://github.com/Molunerfinn/PicGo/releases (訪問較慢)
- 山東大學映象網站:https://mirrors.sdu.edu.cn/github-release/Molunerfinn_PicGo/ (速度較快)
(我的電腦:Windows11,64位,故下載PicGo-Setup-2.3.1-x64.exe
)
下載之後正常安裝就可以了。
安裝lankong外掛
這是一個為 蘭空圖床適配開發的 PicGo 圖片上傳外掛。同樣適用於水墨映客圖床
在picGo的外掛設定中搜尋lankong,然後安裝。
安裝後配置
-
Lsky Pro Version
在下拉選單中選擇 Lsky Pro 版本,V1
還是V2
,預設V1
-
填寫圖床的
server url
,注意不要以/
結束https://image.example.com
✅️https://image.example.com/
❌️
-
填寫
Auth Token
使用Bearer
拼接,token在水墨映客的設定中獲取,注意在配置時候需要前面拼接Bearer
-
Strategy ID
,儲存策略 ID,如果是 V1 或 V2 使用預設儲存策略的使用者,請留空;除非你知道具體 ID,否則請留空 -
Album ID
,相簿 ID,只針對 V2 有效 -
Permission
,圖片許可權,公開還是私有,預設是私有 -
Sync Delete
同步刪除選項,只支援V2
,開啟後在 PicGo 相簿中刪除圖片可同步刪除圖床上的檔案,預設關閉 -
Ignore certificate error
開關,預設關閉,請保持關閉,除非你遇到certificate has expired
等證書報錯才需要考慮將其開啟。由於有些站點使用 Let's Encrypt 頒發的免費證書,有效期只有 90 天,在測試上傳中遇到了certificate has expired
錯誤,開啟開關Ignore certificate error
即可成功上傳
配置完畢儲存即可,然後再typora裡配置圖片上傳服務即可。
在SpringBoot中開發圖片上傳工具類
該工具類的方法有兩個引數一個是檔名(fileName),另一個是檔案的輸入流。
在Spring Boot中一般使用MultipartFile類來處理檔案相關,在此處可以透過getInputStream()方法獲取檔案的輸入流
主要的步驟分為:
- 設定圖片上傳請求的引數
- 設定圖片的引數
- 讀取圖片內容並寫入請求
- 獲取響應
水墨映客圖床的上傳介面為https://img.ink/api/upload
,如果上傳成功狀態碼返回200,失敗返回500
上傳成功的返回格式:
{
"code":200,
"msg":"success",
"data":
{
"id":"281381",
"name":".jpg",
"url":"https:\/\/pic2.ziyuan.wang\/user\/userNickName\/2024\/04\/fileName_d75b474147589.jpg", "size":101168,
"mime":"image\/jpeg",
"sha1":"9adb76331eb1254567547afea3e516ac1901eac1",
"md5":"aef7bc728fd44e77d5688529c8122882",
"quota":"6485442560.00",
"use_quota":"6666141.00"
},
"time":1714282312
}
設定圖片上傳請求的引數
// 設定上傳地址
URL url = new URL("https://img.ink/api/upload");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 設定請求方法為POST
connection.setRequestMethod("POST");
connection.setDoOutput(true);
// 設定請求頭
connection.setRequestProperty("token", "你的請求頭"); // 替換為你的token,在設定中獲取
// 構建請求引數
String boundary = Long.toHexString(System.currentTimeMillis());//用來標識multipart/form-data內容型別的邊界字串,使用時間戳,確保每次請求的邊界字串都是唯一的,以避免衝突
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);//設定上傳檔案的格式
OutputStream output = connection.getOutputStream();//獲取與HTTP連線關聯的輸出流
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);//向輸出流寫入文字形式的資料
設定圖片的引數
writer.append("--" + boundary).append("\r\n");//新增了一個分隔邊界標記
writer.append("Content-Disposition: form-data; name=\"image\"; filename=\"" + fileName + "\"").append("\r\n");//設定Content-Disposition頭部資訊,
writer.append("Content-Type: image/jpeg").append("\r\n");//宣告上傳檔案的MIME型別為image/jpeg
writer.append("\r\n");//回車換行符作為分隔
writer.flush();
讀取圖片內容並寫入請求
//接受傳入的輸入流
InputStream input = inputStream;
byte[] buffer = new byte[4096];//臨時儲存從輸入流中讀取的資料
int bytesRead;
//持續讀取輸入流中的資料直到沒有資料
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
output.flush();
input.close();
// 結束整個multipart/form-data請求體
writer.append("\r\n").append("--" + boundary + "--").append("\r\n");
writer.close();
獲取響應
//獲取伺服器對上傳請求的響應狀態碼,200代表上傳成功
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuffer response = new StringBuffer();
//逐行讀取伺服器返回的文字資料
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
ObjectMapper mapper = new ObjectMapper();
//使用Jackson庫的ObjectMapper來解析response中的JSON字串為JsonNode物件
JsonNode rootNode = mapper.readTree(String.valueOf(response));
//從解析出的JSON物件中,透過路徑rootNode.path("data")定位到"data"節點,再從該節點下獲取"url"欄位的值,即圖片上傳後的URL
JsonNode userNode = rootNode.path("data");
String imgUrl = userNode.path("url").asText();
return imgUrl;
} else {
return "error";
}
呼叫該方法時的使用(file為MultipartFile物件):
String originalFilename = file.getOriginalFilename();//獲取完整檔名
String fileName = originalFilename.substring(originalFilename.lastIndexOf("."));//獲取檔名字尾
String url = InkImageUtil.uploadImg(fileName,file.getInputStream());//呼叫方法上傳圖片
測試:
原始碼:
public class InkImageUtil {
public static String uploadImg(String fileName,InputStream inputStream) throws IOException {
// 設定上傳地址
URL url = new URL("https://img.ink/api/upload");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
// 設定請求方法為POST
connection.setRequestMethod("POST");
connection.setDoOutput(true);
// 設定請求頭
connection.setRequestProperty("token", "f3ee57bcc8d796ea9b72bd776f243d98"); // 替換為你的token
// 構建請求引數
String boundary = Long.toHexString(System.currentTimeMillis());
connection.setRequestProperty("Content-Type", "multipart/form-data; boundary=" + boundary);
OutputStream output = connection.getOutputStream();
PrintWriter writer = new PrintWriter(new OutputStreamWriter(output, "UTF-8"), true);
// 新增圖片引數
writer.append("--" + boundary).append("\r\n");
writer.append("Content-Disposition: form-data; name=\"image\"; filename=\"" + fileName + "\"").append("\r\n");
writer.append("Content-Type: image/jpeg").append("\r\n");
writer.append("\r\n");
writer.flush();
// 讀取圖片內容並寫入請求
InputStream input = inputStream;
byte[] buffer = new byte[4096];
int bytesRead;
while ((bytesRead = input.read(buffer)) != -1) {
output.write(buffer, 0, bytesRead);
}
output.flush();
input.close();
// 結束請求
writer.append("\r\n").append("--" + boundary + "--").append("\r\n");
writer.close();
// 獲取響應
int responseCode = connection.getResponseCode();
if (responseCode == HttpURLConnection.HTTP_OK) {
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line;
StringBuffer response = new StringBuffer();
while ((line = reader.readLine()) != null) {
response.append(line);
}
reader.close();
System.out.println(response.toString());
ObjectMapper mapper = new ObjectMapper();
JsonNode rootNode = mapper.readTree(String.valueOf(response));
JsonNode userNode = rootNode.path("data");
String imgUrl = userNode.path("url").asText();
return imgUrl;
} else {
return "error";
}
}
}
@PostMapping("/upload")
public Result<String> fileUpload(MultipartFile file) throws IOException {
String originalFilename = file.getOriginalFilename();
String fileName = originalFilename.substring(originalFilename.lastIndexOf("."));
String url = InkImageUtil.uploadImg(fileName,file.getInputStream());
return Result.success(url);
}
歡迎關注公眾號:愚生淺末