微服務編排Fizz Gateway是如何支持服務編排上傳文件

#實例說明

一般文件上傳或圖片服務都是一個獨立的服務來維護,它只專一於文件的上傳和維護,不處理具體的業務邏輯。它會提供一個文件上傳接口,上傳成功後接口返回文件的URL(或文件ID)供業務方使用,業務方只須要存儲文件的URL;下面以修改我的信息場景爲例說明:html

  • 用戶能夠修更名稱,年齡和頭像
  • 後端服務已有公用的通用文件上傳接口,經過multipart/form-data提交, 如:/post/fileUpload
  • 後端服務已有保存用戶信息接口,如:/post/saveProfile,入參爲(userId, name, age, avatarUrl)

爲了實現這個功能在往常咱們需求編寫一個更新我的信息接口,先調用/post/fileUpload上傳圖片,拿到圖片URL後再調用/post/saveProfile接口保存到用戶表。前端

下面以這個場景來編排一個更新我的信息的接口java

#環境準備

建立一個服務來模擬已有的接口,項目代碼:github.com/wehotel/fiz…react

FileUploadController.java:git

package we.controller;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.springframework.http.codec.multipart.FilePart;
import org.springframework.http.codec.multipart.FormFieldPart;
import org.springframework.http.codec.multipart.Part;
import org.springframework.util.MultiValueMap;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.server.ServerWebExchange;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * Example
 * 
 * @author Francis Dong
 *
 */
@RestController
public class FileUploadController {

	/**
	 * 
	 * File upload
	 * 
	 * @param exchange
	 * @return
	 */
	@PostMapping(value = "/post/fileUpload", consumes = "multipart/form-data")
	public Object formData(ServerWebExchange exchange) {
		Mono<MultiValueMap<String, Part>> m = exchange.getMultipartData();
		return m.flatMap(md -> {
			// extract non-file form data
			Map<String, Object> params = extractFormData(md);
			System.out.println(params);

			List<FilePart> list = new ArrayList<>();
			for (Entry<String, List<Part>> entry : md.entrySet()) {
				List<Part> val = entry.getValue();
				if (val != null && val.size() > 0) {
					val.stream().forEach(part -> {
						if (part instanceof FilePart) {
							FilePart fp = (FilePart) part;
							list.add(fp);
						}
					});
				}
			}

			if (list != null && list.size() > 0) {
				Flux<FilePart> fileParts = Flux.fromIterable(list);
				return fileParts.flatMap(fp -> {
					String tmpPath = System.getProperty("user.dir") + "/tmp/";
					File tmpFolder = new File(tmpPath);
					if (!tmpFolder.exists()) {
						tmpFolder.mkdirs();
					}
					String path = tmpPath + fp.filename();
					System.out.println(path);

					// Return file path
					return fp.transferTo(new File(path)).then(Mono.just("/tmp/" + fp.filename()));
				}).collectList().flatMap(urls -> {
					Map<String, Object> result = new HashMap<>();
					result.put("urls", urls);
					return Mono.just(result);
				});

			} else {
				return Mono.just(new HashMap<>());
			}
		});
	}

	/**
	 * Save profile
	 * 
	 * @param exchange
	 * @return
	 */
	@PostMapping(value = "/post/saveProfile", consumes = "application/x-www-form-urlencoded")
	public Object saveProfile(ServerWebExchange exchange) {
		Mono<MultiValueMap<String, String>> m = exchange.getFormData();
		return m.flatMap(md -> {
			// save profile info ...
			System.out.println(md);

			Map<String, Object> result = new HashMap<>();
			result.put("code", 0);
			result.put("message", 0);
			Map<String, Object> user = new HashMap<>();
			user.put("userId", md.getFirst("userId"));
			user.put("name", md.getFirst("name"));
			user.put("age", md.getFirst("age"));
			user.put("avatarUrl", md.getFirst("avatarUrl"));
			result.put("user", user);
			return Mono.just(result);
		});
	}

	public static Map<String, Object> extractFormData(MultiValueMap<String, Part> params) {
		HashMap<String, Object> m = new HashMap<>();

		if (params == null || params.isEmpty()) {
			return m;
		}

		for (Entry<String, List<Part>> entry : params.entrySet()) {
			List<Part> val = entry.getValue();
			if (val != null && val.size() > 0) {
				if (val.size() > 1) {
					List<String> formFieldValues = new ArrayList<>();
					val.stream().forEach(part -> {
						if (part instanceof FormFieldPart) {
							FormFieldPart p = (FormFieldPart) part;
							formFieldValues.add(p.value());
						} else if (part instanceof FilePart) {
							FilePart fp = (FilePart) part;
							formFieldValues.add(fp.filename());
						}
					});
					if (formFieldValues.size() > 0) {
						m.put(entry.getKey(), formFieldValues);
					}
				} else {
					if (val.get(0) instanceof FormFieldPart) {
						FormFieldPart p = (FormFieldPart) val.get(0);
						m.put(entry.getKey(), p.value());
					} else if (val.get(0) instanceof FilePart) {
						FilePart fp = (FilePart) val.get(0);
						m.put(entry.getKey(), fp.filename());
					}
				}
			}
		}
		return m;
	}
}


複製代碼

#編排更新我的信息接口

#新增接口

菜單位置:服務編輯->接口列表,點擊新增spring

#配置輸入

在配置輸入tab能夠定義接口的入參和請求頭等信息,若是不定義網關不會對接收到的參數作任何校驗。後端

#配置步驟

由於要前後調用兩個接口,需求新增2個步驟. 在步驟1裏調用圖片上傳接口,在步驟2裏調用保存用戶信息接口。api

步驟一:

點擊新增HTTP服務,把上傳文件服務和保存用戶信息的服務添加到系統。

選擇剛添加的服務fizz-examples-rest-api,填寫上傳文件接口路徑/post/fileUpload,請求體裏選form-data,引用用戶輸入的圖片參數。

步驟二:

添加步驟二,選fizz-examples-rest-api服務,填寫保存用戶信息接口路徑/post/saveProfile,引用步驟一上傳文件接口的返回結果和用戶輸入的姓名和年齡數據。

#配置輸出

配置要返回給前端的響應報文,這裏直接引用步驟二的結果

#測試

配置完接口後,點擊測試

#訪問正式接口

發佈接口後訪問URL: http://[網關IP]:8600/proxy/func-test/user/updateProfile

版本要求

Fizz網關v2.1.0或以上版本 (安裝教程(opens new window))

Fizz網關從1.0開始已支持文件上傳請求的轉發,從2.1.0開始在服務編排功能對form-data上傳文件進行了支持,以便進行更復雜的接口編排。

相關文章
相關標籤/搜索