MG Mediagate

Internal Image Gateway

Mediagate 接入指南

Mediagate 是内部文件/图片上传与临时访问服务。应用用 App Token 调用上传接口,服务返回 imageId 和 8 小时临时访问地址;后台支持按应用和命名空间检索。

认证方式Authorization: Bearer <App Token>
上传字段multipart 表单中只能有一个 file 字段
访问方式用返回的 imageId 生成临时 URL,默认有效 8 小时

1. 准备应用

  1. 登录后台

    访问 /admin,由管理员为你的账号开通访问权限;具备创建应用权限后即可新建应用。

  2. 创建应用

    后台会自动生成 App IDnamespaceApp Token。namespace 不需要手填,避免冲突和非法字符。

  3. 保存 Token

    App Token 用于接口认证。重新生成 Token 会让旧 Token 立即失效,后台会要求二次确认。

2. 上传文件/图片

请求路径中的 {namespace} 使用后台应用凭证里显示的命名空间。

curl -X POST 'https://mediagate.nianjk.com/api/v1/namespaces/{namespace}/images' \
  -H 'Authorization: Bearer <App Token>' \
  -F 'file=@./sample.png'

支持逐个文件上传。每次上传成功都会返回 imageId;图片会生成后台缩略预览,非图片文件只保存元数据和临时访问地址。

成功响应

{
  "namespace": "{namespace}",
  "imageId": "{imageId}",
  "accessUrl": "https://mediagate.nianjk.com/t/...",
  "expiresAt": "2026-05-09T22:30:00Z",
  "contentType": "image/png",
  "size": 12345
}
临时访问地址

上传响应里的 accessUrl 可直接用于预览或写入业务结果;业务系统长期保存 imageId 即可,过期后按需重新生成访问地址。

Token 使用边界

App Token 只放在内部服务端或配置中心。浏览器和前端页面只接收已经生成好的临时 URL。

3. 通过 imageId 再生成临时 URL

curl -X POST 'https://mediagate.nianjk.com/api/v1/namespaces/{namespace}/images/{imageId}/access-url' \
  -H 'Authorization: Bearer <App Token>'
{
  "url": "https://mediagate.nianjk.com/t/{namespace}/{imageId}?expires=...",
  "accessUrl": "https://mediagate.nianjk.com/t/{namespace}/{imageId}?expires=...",
  "expiresAt": "2026-05-09T22:30:00Z"
}

4. 语言样例

下面的示例都只依赖应用凭证里的 namespaceApp Token。示例中的 {namespace}{imageId}mgtk_app_xxx.<token> 均为占位值,接入时请替换成后台凭证中的真实值;生产环境建议把 Token 放在服务端环境变量或配置中心,不要写入前端代码。

Python:上传并按 imageId 获取临时 URL

requests
依赖

pip install requests

上传

POST /api/v1/namespaces/{namespace}/images,表单字段为 file

获取临时 URL

保存上传返回的 imageId,需要访问时再生成 8 小时 URL。

python / requests
import requests

BASE_URL = "https://mediagate.nianjk.com"
NAMESPACE = "{namespace}"
APP_TOKEN = "mgtk_app_xxx.<token>"


def upload_image(path):
    url = f"{BASE_URL}/api/v1/namespaces/{NAMESPACE}/images"
    headers = {"Authorization": f"Bearer {APP_TOKEN}"}
    with open(path, "rb") as file_obj:
        files = {"file": file_obj}
        resp = requests.post(url, headers=headers, files=files, timeout=30)
    resp.raise_for_status()
    return resp.json()


def create_access_url(image_id):
    url = f"{BASE_URL}/api/v1/namespaces/{NAMESPACE}/images/{image_id}/access-url"
    headers = {"Authorization": f"Bearer {APP_TOKEN}"}
    resp = requests.post(url, headers=headers, timeout=10)
    resp.raise_for_status()
    return resp.json()


uploaded = upload_image("./sample.png")
print("imageId:", uploaded["imageId"])
print("upload accessUrl:", uploaded.get("accessUrl"))

access = create_access_url(uploaded["imageId"])
print("new accessUrl:", access["accessUrl"])

Go:上传并按 imageId 获取临时 URL

stdlib
依赖

只使用 Go 标准库,适合服务端直接集成。

上传

multipart.NewWriter 构造单文件表单,Header 带 Bearer Token。

获取临时 URL

用上传返回的 imageId 调访问地址接口。

go / net/http
package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"os"
)

const baseURL = "https://mediagate.nianjk.com"
const namespace = "{namespace}"
const appToken = "mgtk_app_xxx.<token>"

type UploadResponse struct {
	ImageID   string `json:"imageId"`
	AccessURL string `json:"accessUrl"`
}

type AccessURLResponse struct {
	AccessURL string `json:"accessUrl"`
	ExpiresAt string `json:"expiresAt"`
}

func uploadImage(path string) (UploadResponse, error) {
	file, err := os.Open(path)
	if err != nil {
		return UploadResponse{}, err
	}
	defer file.Close()

	var body bytes.Buffer
	writer := multipart.NewWriter(&body)
	part, err := writer.CreateFormFile("file", path)
	if err != nil {
		return UploadResponse{}, err
	}
	if _, err := io.Copy(part, file); err != nil {
		return UploadResponse{}, err
	}
	if err := writer.Close(); err != nil {
		return UploadResponse{}, err
	}

	url := baseURL + "/api/v1/namespaces/" + namespace + "/images"
	req, err := http.NewRequest(http.MethodPost, url, &body)
	if err != nil {
		return UploadResponse{}, err
	}
	req.Header.Set("Authorization", "Bearer "+appToken)
	req.Header.Set("Content-Type", writer.FormDataContentType())

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return UploadResponse{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		return UploadResponse{}, fmt.Errorf("upload failed: %s", resp.Status)
	}

	var out UploadResponse
	return out, json.NewDecoder(resp.Body).Decode(&out)
}

func createAccessURL(imageID string) (AccessURLResponse, error) {
	url := baseURL + "/api/v1/namespaces/" + namespace + "/images/" + imageID + "/access-url"
	req, err := http.NewRequest(http.MethodPost, url, nil)
	if err != nil {
		return AccessURLResponse{}, err
	}
	req.Header.Set("Authorization", "Bearer "+appToken)

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return AccessURLResponse{}, err
	}
	defer resp.Body.Close()
	if resp.StatusCode >= 300 {
		return AccessURLResponse{}, fmt.Errorf("access url failed: %s", resp.Status)
	}

	var out AccessURLResponse
	return out, json.NewDecoder(resp.Body).Decode(&out)
}

func main() {
	uploaded, err := uploadImage("./sample.png")
	if err != nil {
		panic(err)
	}
	fmt.Println("imageId:", uploaded.ImageID)
	fmt.Println("upload accessUrl:", uploaded.AccessURL)

	access, err := createAccessURL(uploaded.ImageID)
	if err != nil {
		panic(err)
	}
	fmt.Println("new accessUrl:", access.AccessURL)
}

Node.js:上传并按 imageId 获取临时 URL

axios
依赖

npm install axios form-data

上传

使用 form-data 追加 file,并合并表单 Header。

获取临时 URL

POST 到 /access-url,响应里的 accessUrl 可直接给浏览器使用。

node.js / axios
const fs = require("fs");
const axios = require("axios");
const FormData = require("form-data");

const BASE_URL = "https://mediagate.nianjk.com";
const NAMESPACE = "{namespace}";
const APP_TOKEN = "mgtk_app_xxx.<token>";

async function uploadImage(path) {
  const form = new FormData();
  form.append("file", fs.createReadStream(path));

  const resp = await axios.post(
    BASE_URL + "/api/v1/namespaces/" + NAMESPACE + "/images",
    form,
    {
      timeout: 30000,
      headers: {
        ...form.getHeaders(),
        Authorization: "Bearer " + APP_TOKEN
      }
    }
  );
  return resp.data;
}

async function createAccessUrl(imageId) {
  const resp = await axios.post(
    BASE_URL + "/api/v1/namespaces/" + NAMESPACE + "/images/" + imageId + "/access-url",
    null,
    {
      timeout: 10000,
      headers: { Authorization: "Bearer " + APP_TOKEN }
    }
  );
  return resp.data;
}

(async () => {
  const uploaded = await uploadImage("./sample.png");
  console.log("imageId:", uploaded.imageId);
  console.log("upload accessUrl:", uploaded.accessUrl);

  const access = await createAccessUrl(uploaded.imageId);
  console.log("new accessUrl:", access.accessUrl);
})();

权限边界

角色 / 凭证能做什么不能做什么
App Token上传文件/图片、读取本应用授权 namespace 下的文件/图片、生成临时 URL登录后台、管理用户、重生成 Token
应用所有者查看自己应用和图片,管理自己的应用凭证查看未授权应用
被授权用户查看被授权应用和图片创建应用、删除图片、重生成 App Token
超级管理员管理用户授权、应用、命名空间和存储监控-

常见错误

状态码常见原因处理方式
401缺少 Authorization 或 Token 无效确认使用 Bearer <App Token>,必要时在后台查看或重新生成 Token。
403应用没有该 namespace 的读写权限检查应用凭证中的 namespace,或联系管理员授权。
404namespace、文件或临时 URL 不存在 / 已删除 / 已过期确认 imageId 和 namespace;过期后重新生成临时 URL。
413文件超过上传大小限制压缩文件或联系管理员调整限制。