> ## Documentation Index
> Fetch the complete documentation index at: https://www.ayrshare.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# 上傳大型媒體檔案

> 若要上傳大於 10 MB 的檔案，請先取得預簽章 URL 以上傳檔案

export const HeaderAPI = ({noProfileKey, profileKeyRequired}) => <>
    <ParamField header="Authorization" type="string" required>
      <a href="/apis/overview#authorization">API Key</a> of the Primary Profile.
      <br />
      <br />
      Format: <code>Authorization: Bearer API_KEY</code>
    </ParamField>
    {!noProfileKey && (profileKeyRequired ? <ParamField header="Profile-Key" type="string" required>
          <a href="/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField> : <ParamField header="Profile-Key" type="string">
          <a href="/apis/overview#profile-key-format">Profile Key</a> of a User Profile.
          <br />
          <br />
          Format: <code>Profile-Key: PROFILE_KEY</code>
        </ParamField>)}
  </>;

export const PlansAvailable = ({plans = [], maxPackRequired}) => {
  let displayPlans = plans;
  if (plans && plans.length === 1) {
    const lowerCasePlan = plans[0].toLowerCase();
    if (lowerCasePlan === "business") {
      displayPlans = ["Launch", "Business", "Enterprise"];
    } else if (lowerCasePlan === "premium") {
      displayPlans = ["Premium", "Launch", "Business", "Enterprise"];
    }
  }
  return <Note>
Available on {displayPlans.length === 1 ? "the " : ""}
{displayPlans.join(", ").replace(/\b\w/g, l => l.toUpperCase())}{" "}
{displayPlans.length > 1 ? "plans" : "plan"}.

{maxPackRequired && <span onClick={() => window.open('https://www.ayrshare.com/docs/additional/maxpack', '_self')} className="flex items-center mt-2 cursor-pointer">
 <span className="px-1.5 py-0.5 rounded text-sm" style={{
    backgroundColor: '#C264B6',
    color: 'white',
    fontSize: '12px'
  }}>
   Max Pack required
 </span>
</span>}
</Note>;
};

<PlansAvailable plans={["premium"]} maxPackRequired={false} />

若要上傳大於 10 MB 的檔案，請先取得預簽章 URL 以上傳檔案。

<ul class="custom-bullets">
  <li>最大檔案上傳大小 5 GB。</li>
  <li>上傳用的預簽章 URL 在產生後 30 分鐘內有效。</li>

  <li>
    上傳完成後的存取 URL 有效期為 30 天。所有已發布的貼文在社群網路上不受影響。已排程超過該時間範圍的貼文，在發布時將發生錯誤。
  </li>
</ul>

<Tip>如果您的媒體已可透過外部 URL（例如 S3 儲存桶）存取，就不必再將檔案上傳到 Ayrshare。只要在 POST 到 `/post` 端點時，將外部可存取的 URL 放入 `mediaURLs` 請求主體參數，您的檔案就會被自動上傳。</Tip>

## 標頭參數

<HeaderAPI noProfileKey={true} />

## 查詢參數

<ParamField query="fileName" type="string">
  若未提供 contentType，則必須提供包含副檔名的完整檔案名稱。

  要上傳的檔案名稱。必須包含副檔名，例如 .png、.jpg、.mov、.mp4 等。
</ParamField>

<ParamField query="contentType" type="string">
  所上傳媒體的 content-type。有效格式包含：`mp4`、`mov`、`png`、`jpg` 或 `jpeg`。

  例如，若檔案是 Quicktime 的 .mov 檔，則 contentType 應設為 `mov`。

  若未提供，將使用 application/octet-stream。
</ParamField>

<RequestExample>
  ```bash cURL theme={"system"}
  curl \
  -H "Authorization: Bearer [API Key]" \
  -X GET https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov
  ```

  ```javascript JavaScript theme={"system"}
  const API_KEY = "Your API Key";

  fetch("https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov", {
        method: "GET",
        headers: {
          "Authorization": `Bearer ${API_KEY}`
        }
      })
        .then((res) => res.json())
        .then((json) => console.log(json))
        .catch(console.error);
  ```

  ```python Python theme={"system"}
  import requests

  headers = {'Authorization': 'Bearer [API_KEY]'}

  r = requests.get('https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov', headers=headers)

  print(r.json())
  ```

  ```php PHP theme={"system"}
  <?php
  require 'vendor/autoload.php';    // Composer auto-loader using Guzzle. See .../guzzlephp.org/en/stable/overview.html

  $client = new GuzzleHttp\Client();
  $res = $client->request(
      'GET',
      'https://api.ayrshare.com/api/media/uploadUrl?fileName=test.mov&contentType=mov',
      [
          'headers' => [
              'Content-Type'      => 'application/json',
              'Authorization'     => 'Bearer API_KEY'
          ]
      ]
  );

  echo json_encode(json_decode($res->getBody()), JSON_PRETTY_PRINT);
  ```

  ```csharp C# theme={"system"}
  using System;
  using System.Net.Http;
  using System.Threading.Tasks;
  using System.Web;

  namespace MediaUploadUrlGETRequest_csharp
  {
      class MediaUploadUrl
      {
          static async Task Main(string[] args)
          {
              string API_KEY = "API_KEY";
              string baseUrl = "https://api.ayrshare.com/api/media/uploadUrl";

              // Build URL with query parameters
              var uriBuilder = new UriBuilder(baseUrl);
              var query = HttpUtility.ParseQueryString(string.Empty);
              query["fileName"] = "test.mov";
              query["contentType"] = "mov";
              uriBuilder.Query = query.ToString();

              using (var client = new HttpClient())
              {
                  client.DefaultRequestHeaders.Add("Authorization", "Bearer " + API_KEY);

                  try
                  {
                      HttpResponseMessage response = await client.GetAsync(uriBuilder.Uri);
                      response.EnsureSuccessStatusCode();
                      string responseBody = await response.Content.ReadAsStringAsync();
                      Console.WriteLine(responseBody);
                  }
                  catch (HttpRequestException e)
                  {
                      Console.WriteLine($"Error: {e.Message}");
                  }
              }
          }
      }
  }
  ```
</RequestExample>

## 回應細節

<ul class="custom-bullets">
  <li>`accessUrl` 是上傳完成後用來存取媒體檔案的 URL。</li>

  <li>
    `contentType` 是所上傳媒體所設定的 content-type。上傳媒體時請在 *Content-Type* 標頭中使用此值。
  </li>

  <li>`uploadUrl` 是用來對媒體檔案發出 *PUT* 請求的 URL。請參見下方說明。</li>
</ul>

### 其他端點範例

上傳較大檔案的流程：

<ul class="custom-bullets">
  <li>
    透過 `/media/uploadUrl` 端點取得 `uploadURL` 與 `accessURL`。請參見上方說明。
  </li>

  <li>透過 *PUT* 上傳檔案，並將 *Content-Type* 設定為所回傳的 `contentType`。</li>
  <li>使用 `--upload-file` 搭配媒體檔案及 `uploadUrl` 來上傳媒體。</li>
  <li>上傳成功時，會回傳 `200` 回應。</li>

  <li>
    上傳媒體檔案後，將 `accessUrl` 放入 `/post` 端點的 `mediaUrls` 主體參數中執行 POST。
  </li>

  <li>
    **預簽章上傳 URL 僅能上傳一次**。若您傳送了錯誤的檔案，就必須重新建立新的上傳 URL。若檔案未成功上傳，將不會收到錯誤回應。
    請參見下方[驗證 URL 是否存在](/apis/media/verify-media-url)。
  </li>
</ul>

<CodeGroup>
  ```bash cURL theme={"system"}
  curl -X PUT \
  -H 'Content-Type: video/mp4' \
  --upload-file LOCAL_FILE_PATH uploadUrl
  ```

  ```javascript JavaScript theme={"system"}
  const fs = require("fs").promises;
  const uploadFileToSignedUrl = async (signedUrl, filePath) => {
      try {
        const fileBuffer = await fs.readFile(filePath);
        const response = await fetch(signedUrl, {
          method: "PUT",
          body: fileBuffer,
          headers: {
            "Content-Type": "video/mp4"
          }
        });

        if (response.ok) {
          console.log("File upload successful:", response.status);
        } else {
          console.error("File upload failed:", response.status);
        }
      } catch (error) {
        console.error("Error uploading file:", error);
      }
  };

  // Use the signed URL generated from the previous step
  const signedUrl = "SIGNED_URL";
  const filePath = "LOCAL_FILE_PATH";

  uploadFileToSignedUrl(signedUrl, filePath);
  ```

  ```python Python theme={"system"}
  import requests

  def upload_file_to_signed_url(signed_url, file_path):
      try:
          with open(file_path, 'rb') as file:
              response = requests.put(signed_url, data=file, headers={'Content-Type': 'video/mp4'})

          if response.ok:
              print("File upload successful:", response.status_code)
          else:
              print("File upload failed:", response.status_code)
      except Exception as error:
          print("Error uploading file:", error)

  # Use the signed URL generated from the previous step
  signed_url = "SIGNED_URL"
  file_path = "LOCAL_FILE_PATH"

  upload_file_to_signed_url(signed_url, file_path)
  ```

  ```php PHP theme={"system"}
  <?php

  function uploadFileToSignedUrl($signedUrl, $filePath) {
      try {
          $fileHandle = fopen($filePath, 'rb');
          if ($fileHandle === false) {
              throw new Exception('Cannot open the file');
          }

          $ch = curl_init($signedUrl);

          curl_setopt($ch, CURLOPT_PUT, true);
          curl_setopt($ch, CURLOPT_INFILE, $fileHandle);
          curl_setopt($ch, CURLOPT_INFILESIZE, filesize($filePath));
          curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: video/mp4'));
          curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);

          $response = curl_exec($ch);
          $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);

          if ($httpCode == 200) {
              echo "File upload successful: " . $httpCode;
          } else {
              echo "File upload failed: " . $httpCode;
          }

          fclose($fileHandle);
          curl_close($ch);
      } catch (Exception $e) {
          echo "Error uploading file: " . $e->getMessage();
      }
  }

  // Use the signed URL generated from the previous step
  $signedUrl = "SIGNED_URL";
  $filePath = "LOCAL_FILE_PATH";

  uploadFileToSignedUrl($signedUrl, $filePath);
  ?>

  ```

  ```csharp C# theme={"system"}
  using System;
  using System.Net.Http;
  using System.IO;
  using System.Threading.Tasks;

  class Program
  {
      static async Task Main(string[] args)
      {
          string signedUrl = "SIGNED_URL"; // Replace with your signed URL
          string filePath = "LOCAL_FILE_PATH"; // Replace with your file path

          try
          {
              await UploadFileToSignedUrl(signedUrl, filePath);
          }
          catch (Exception ex)
          {
              Console.WriteLine("Error uploading file: " + ex.Message);
          }
      }

      static async Task UploadFileToSignedUrl(string signedUrl, string filePath)
      {
          using (var client = new HttpClient())
          using (var fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read))
          using (var content = new StreamContent(fileStream))
          {
              content.Headers.Add("Content-Type", "video/mp4");

              var response = await client.PutAsync(signedUrl, content);

              if (response.IsSuccessStatusCode)
              {
                  Console.WriteLine("File upload successful: " + response.StatusCode);
              }
              else
              {
                  Console.WriteLine("File upload failed: " + response.StatusCode);
              }
          }
      }
  }

  ```

  ```java Java theme={"system"}
  import java.net.URI;
  import java.net.http.HttpClient;
  import java.net.http.HttpRequest;
  import java.net.http.HttpResponse;
  import java.net.http.HttpRequest.BodyPublishers;
  import java.nio.file.Path;
  import java.io.IOException;
  import java.nio.file.Files;

  public class FileUploader {

      public static void main(String[] args) {
          String signedUrl = "SIGNED_URL"; // Replace with your signed URL
          String filePath = "LOCAL_FILE_PATH"; // Replace with your file path

          try {
              uploadFileToSignedUrl(signedUrl, filePath);
          } catch (IOException | InterruptedException e) {
              System.out.println("Error uploading file: " + e.getMessage());
          }
      }

      private static void uploadFileToSignedUrl(String signedUrl, String filePath) throws IOException, InterruptedException {
          HttpClient client = HttpClient.newHttpClient();
          HttpRequest request = HttpRequest.newBuilder()
                  .uri(URI.create(signedUrl))
                  .header("Content-Type", "image/jpg")
                  .PUT(BodyPublishers.ofFile(Path.of(filePath)))
                  .build();

          HttpResponse<String> response = client.send(request, HttpResponse.BodyHandlers.ofString());

          if (response.statusCode() == 200) {
              System.out.println("File upload successful: " + response.statusCode());
          } else {
              System.out.println("File upload failed: " + response.statusCode());
          }
      }
  }

  ```
</CodeGroup>

請務必確保建立 `uploadUrl` 時所設定的 `contentType`，與 PUT 檔案時的 Content-Type 及檔案類型一致。
例如，建立 `uploadUrl` 時若將 `contentType` 設為 "image/png"，則上傳時應設定 `Content-Type: image/png`，且上傳的檔案應以 `.png` 結尾。

上傳成功時，會回傳 `200` 回應。

### 在 Node.js 中上傳二進位檔案範例

以下是使用 Node.js 搭配 JavaScript 上傳二進位媒體檔案的範例：

```javascript theme={"system"}
const fs = require("fs");
const request = require("request");

const API_KEY = "Your API Key";
const fileName = "test.png";
const endpoint = `https://api.ayrshare.com/api/media/uploadUrl?fileName=${fileName}&contentType=png`;

const run = async () => {
  request.get(
    {
      headers: {
        "Content-Type": "application/json",
        Authorization: `Bearer ${API_KEY}`
      },
      url: endpoint
    },
    (err, res, body) => {
      if (err) {
        return console.error(err);
      }

      const json = JSON.parse(body);
      console.log("Upload URL:", json);

      return fs.createReadStream(`./${fileName}`).pipe(
        request.put(
          json.uploadUrl,
          {
            headers: {
              "Content-Type": json.contentType
            }
          },
          (err, httpsResponse, body) => {
            if (err) {
              console.error("err", err);
            } else {
              console.log(body);
            }
          }
        )
      );
    }
  );
};

run();
```

### 在 Postman 中上傳二進位檔案範例

您也可以使用 Postman 將二進位檔案上傳到 `uploadUrl`。

在 Postman 中：

1. 選擇 HTTP 方法 `PUT`。
2. 將您的 `uploadUrl` 貼到 url 欄位。注意，此 URL 在一小時後即過期且只能使用一次。若呼叫失敗，就必須重新產生 `uploadUrl`。
3. 在 *Headers* 中，將 `Content-Type` 設為 /uploadUrl 端點回傳的 content type。例如：`Content-Type: image/png`。
4. 選擇 *Body -> binary* 並選擇要上傳的檔案。
5. 按下 **Send**。
6. **重要**：不會出現回傳的回應，因此您應該透過在瀏覽器中開啟 /uploadUrl 端點回傳的 `accessUrl` 來確認是否上傳成功。您也可以使用 [verify URL 端點](/apis/media/verify-media-url)。

<img class="center" src="https://mintcdn.com/ayrshare-docs/Nmrhj2Gh7WSf62Bh/images/apis/validate/postman%20binary.webp?fit=max&auto=format&n=Nmrhj2Gh7WSf62Bh&q=85&s=ca3def769242814afb17a58d9268667f" alt="Postman Binary Upload" width="1536" height="189" data-path="images/apis/validate/postman binary.webp" />

<Card title="Postman 範例 JSON 檔案" icon="file" href="/files/Large Media File Upload.postman_collection.json" horizontal />

<ResponseExample>
  ```javascript 200: An upload URL and access URL theme={"system"}
  {
      "accessUrl": "https://media.ayrshare.com/Aswmfs3dIEbwLSdhTlV2/test.mp4",
      "contentType": "video/mp4",
      "uploadUrl": "https://storage.googleapis.com/..."
  }
  ```

  ```json 400: Bad Request Error getting signed URL theme={"system"}
  {
    "action": "upload",
    "status": "error",
    "code": 301,
    "message": "The provided content-type 'movd' is not recognized."
  }
  ```
</ResponseExample>
