Penyandi dan Penyahkod Base64: Tukar Teks ke/dari Base64
Alat dalam talian percuma untuk menyandi teks ke Base64 atau menyahkod rentetan Base64 kembali ke teks. Menyokong penyandian Base64 standard dan selamat URL dengan penukaran segera.
Penyandi/Penyahkod Base64
Tukar teks kepada dan dari pengekodan Base64
Dokumentasi
Pengekod dan Penyahkod Base64
Pengenalan
Base64 adalah skema pengekodan binari-ke-teks yang mewakili data binari dalam format rentetan ASCII. Ia direka untuk membawa data yang disimpan dalam format binari merentasi saluran yang hanya menyokong kandungan teks dengan boleh dipercayai. Pengekodan Base64 menukarkan data binari menjadi satu set 64 watak (oleh itu namanya) yang boleh dihantar dengan selamat melalui protokol berasaskan teks tanpa kerosakan data.
Set watak Base64 terdiri daripada:
- Huruf besar A-Z (26 watak)
- Huruf kecil a-z (26 watak)
- Digit 0-9 (10 watak)
- Dua watak tambahan, biasanya "+" dan "/" (2 watak)
Alat ini membolehkan anda dengan mudah mengekod teks ke format Base64 atau menyahkod rentetan Base64 kembali kepada teks asalnya. Ia sangat berguna untuk pemaju, profesional IT, dan sesiapa yang bekerja dengan data yang perlu dihantar dengan selamat merentasi saluran berasaskan teks.
Cara Pengekodan Base64 Berfungsi
Proses Pengekodan
Pengekodan Base64 berfungsi dengan menukarkan setiap kumpulan tiga bait (24 bit) data binari menjadi empat watak Base64. Proses ini mengikuti langkah-langkah berikut:
- Tukar teks input kepada representasi binarinya (menggunakan pengekodan ASCII atau UTF-8)
- Kumpulkan data binari ke dalam segmen 24 bit (3 bait)
- Bahagikan setiap segmen 24 bit kepada empat kumpulan 6 bit
- Tukar setiap kumpulan 6 bit kepada watak Base64 yang sepadan
Apabila panjang input tidak boleh dibahagikan dengan 3, padding dengan watak "=" ditambah untuk mengekalkan nisbah 4:3 panjang output kepada input.
Perwakilan Matematik
Untuk urutan bait , watak Base64 yang sepadan dikira sebagai:
Di mana mewakili watak ke- dalam abjad Base64.
Proses Penyahkodan
Penyahkodan Base64 membalikkan proses pengekodan:
- Tukar setiap watak Base64 kepada nilai 6 bitnya
- Gabungkan nilai 6 bit ini
- Kumpulkan bit kepada kumpulan 8 bit (bait)
- Tukar setiap bait kepada watak yang sepadan
Padding
Apabila bilangan bait untuk dikodkan tidak boleh dibahagikan dengan 3, padding digunakan:
- Jika terdapat satu bait yang tinggal, ia ditukarkan kepada dua watak Base64 diikuti dengan "=="
- Jika terdapat dua bait yang tinggal, ia ditukarkan kepada tiga watak Base64 diikuti dengan "="
Contoh
Mari kita kodkan teks "Hello" ke Base64:
- Representasi ASCII bagi "Hello": 72 101 108 108 111
- Representasi binari: 01001000 01100101 01101100 01101100 01101111
- Pengelompokan kepada kumpulan 6 bit: 010010 000110 010101 101100 011011 000110 1111
- Kumpulan terakhir hanya mempunyai 4 bit, jadi kita tambah padding dengan nol: 010010 000110 010101 101100 011011 000110 111100
- Menukarkan kepada perpuluhan: 18, 6, 21, 44, 27, 6, 60
- Mencari dalam abjad Base64: S, G, V, s, b, G, 8
- Hasilnya adalah "SGVsbG8="
Perhatikan padding "=" di akhir kerana panjang input (5 bait) tidak boleh dibahagikan dengan 3.
Formula
Formula umum untuk mengira panjang rentetan yang dikodkan dalam Base64 adalah:
Di mana mewakili fungsi siling (pembulatan ke atas kepada integer terdekat).
Kes Penggunaan
Pengekodan Base64 digunakan secara meluas dalam pelbagai aplikasi:
-
Lampiran E-mel: MIME (Multipurpose Internet Mail Extensions) menggunakan Base64 untuk mengodkan lampiran binari dalam e-mel.
-
URL Data: Menyematkan imej kecil, fon, atau sumber lain secara langsung dalam HTML, CSS, atau JavaScript menggunakan skema URL
data:
. -
Komunikasi API: Menghantar data binari dengan selamat dalam muatan JSON atau format API berasaskan teks lain.
-
Menyimpan Data Binari dalam Format Teks: Apabila data binari perlu disimpan dalam XML, JSON, atau format berasaskan teks lain.
-
Sistem Pengesahan: Pengesahan Asas dalam HTTP menggunakan pengekodan Base64 (walaupun ia bukan untuk keselamatan, hanya untuk pengekodan).
-
Kriptografi: Sebagai sebahagian daripada pelbagai protokol dan sistem kriptografi, sering untuk mengekod kunci atau sijil.
-
Nilai Cookie: Mengesahkan struktur data yang kompleks untuk disimpan dalam cookie.
Alternatif
Walaupun Base64 digunakan secara meluas, terdapat alternatif yang mungkin lebih sesuai dalam situasi tertentu:
-
Base64 Selamat URL: Variasi yang menggunakan "-" dan "_" sebagai ganti "+" dan "/" untuk mengelakkan isu pengkodan URL. Berguna untuk data yang akan disertakan dalam URL.
-
Base32: Menggunakan set 32 watak, menghasilkan output yang lebih panjang tetapi dengan kebolehan bacaan manusia yang lebih baik dan tidak sensitif kepada kes.
-
Pengekodan Hex: Penukaran mudah kepada heksadesimal, yang kurang cekap (menggandakan saiz) tetapi sangat mudah dan banyak disokong.
-
Pemindahan Binari: Untuk fail besar atau apabila kecekapan adalah penting, protokol pemindahan binari langsung seperti HTTP dengan header Content-Type yang sesuai adalah lebih baik.
-
Pemampatan + Base64: Untuk data teks besar, memampatkan sebelum mengekod boleh mengurangkan peningkatan saiz.
-
Penyusunan JSON/XML: Untuk data terstruktur, menggunakan penyusunan JSON atau XML asli mungkin lebih sesuai daripada pengekodan Base64.
Sejarah
Pengekodan Base64 mempunyai akar dalam sistem pengkomputeran awal dan sistem telekomunikasi di mana data binari perlu dihantar melalui saluran yang direka untuk teks.
Spesifikasi rasmi Base64 pertama kali diterbitkan pada tahun 1987 sebagai sebahagian daripada RFC 989, yang mendefinisikan E-mel yang Ditingkatkan Privasi (PEM). Ini kemudian dikemas kini dalam RFC 1421 (1993) dan RFC 2045 (1996, sebagai sebahagian daripada MIME).
Istilah "Base64" berasal dari hakikat bahawa pengekodan menggunakan 64 watak ASCII yang berbeza untuk mewakili data binari. Pilihan 64 watak ini adalah sengaja, kerana 64 adalah kuasa 2 (2^6), yang menjadikan penukaran antara binari dan Base64 cekap.
Seiring berjalannya waktu, beberapa variasi Base64 telah muncul:
- Base64 Standard: Seperti yang ditakrifkan dalam RFC 4648, menggunakan A-Z, a-z, 0-9, +, / dan = untuk padding
- Base64 Selamat URL: Menggunakan - dan _ sebagai ganti + dan / untuk mengelakkan isu pengkodan URL
- Base64 Selamat Nama Fail: Serupa dengan Base64 selamat URL, direka untuk digunakan dalam nama fail
- Base64 yang Diubahsuai untuk IMAP: Digunakan dalam protokol IMAP dengan set watak khas yang berbeza
Walaupun telah berusia lebih tiga dekad, Base64 tetap menjadi alat asas dalam pengkomputeran moden, terutama dengan kebangkitan aplikasi web dan API yang bergantung kepada format data berasaskan teks seperti JSON.
Contoh Kod
Berikut adalah contoh pengekodan dan penyahkodan Base64 dalam pelbagai bahasa pengaturcaraan:
1// Pengekodan/Penyahkodan Base64 JavaScript
2function encodeToBase64(text) {
3 return btoa(text);
4}
5
6function decodeFromBase64(base64String) {
7 try {
8 return atob(base64String);
9 } catch (e) {
10 throw new Error("Rentetan Base64 tidak sah");
11 }
12}
13
14// Contoh penggunaan
15const originalText = "Hello, World!";
16const encoded = encodeToBase64(originalText);
17console.log("Terkod:", encoded); // SGVsbG8sIFdvcmxkIQ==
18
19try {
20 const decoded = decodeFromBase64(encoded);
21 console.log("Didekod:", decoded); // Hello, World!
22} catch (error) {
23 console.error(error.message);
24}
25
1# Pengekodan/Penyahkodan Base64 Python
2import base64
3
4def encode_to_base64(text):
5 # Tukar rentetan kepada bait dan kemudian kodkan
6 text_bytes = text.encode('utf-8')
7 base64_bytes = base64.b64encode(text_bytes)
8 return base64_bytes.decode('utf-8')
9
10def decode_from_base64(base64_string):
11 try:
12 # Tukar rentetan base64 kepada bait dan kemudian dekodkan
13 base64_bytes = base64_string.encode('utf-8')
14 text_bytes = base64.b64decode(base64_bytes)
15 return text_bytes.decode('utf-8')
16 except Exception as e:
17 raise ValueError(f"Rentetan Base64 tidak sah: {e}")
18
19# Contoh penggunaan
20original_text = "Hello, World!"
21encoded = encode_to_base64(original_text)
22print(f"Terkod: {encoded}") # SGVsbG8sIFdvcmxkIQ==
23
24try:
25 decoded = decode_from_base64(encoded)
26 print(f"Didekod: {decoded}") # Hello, World!
27except ValueError as e:
28 print(e)
29
1// Pengekodan/Penyahkodan Base64 Java
2import java.util.Base64;
3import java.nio.charset.StandardCharsets;
4
5public class Base64Example {
6 public static String encodeToBase64(String text) {
7 byte[] textBytes = text.getBytes(StandardCharsets.UTF_8);
8 byte[] encodedBytes = Base64.getEncoder().encode(textBytes);
9 return new String(encodedBytes, StandardCharsets.UTF_8);
10 }
11
12 public static String decodeFromBase64(String base64String) {
13 try {
14 byte[] base64Bytes = base64String.getBytes(StandardCharsets.UTF_8);
15 byte[] decodedBytes = Base64.getDecoder().decode(base64Bytes);
16 return new String(decodedBytes, StandardCharsets.UTF_8);
17 } catch (IllegalArgumentException e) {
18 throw new IllegalArgumentException("Rentetan Base64 tidak sah: " + e.getMessage());
19 }
20 }
21
22 public static void main(String[] args) {
23 String originalText = "Hello, World!";
24 String encoded = encodeToBase64(originalText);
25 System.out.println("Terkod: " + encoded); // SGVsbG8sIFdvcmxkIQ==
26
27 try {
28 String decoded = decodeFromBase64(encoded);
29 System.out.println("Didekod: " + decoded); // Hello, World!
30 } catch (IllegalArgumentException e) {
31 System.err.println(e.getMessage());
32 }
33 }
34}
35
1<?php
2// Pengekodan/Penyahkodan Base64 PHP
3function encodeToBase64($text) {
4 return base64_encode($text);
5}
6
7function decodeFromBase64($base64String) {
8 $decoded = base64_decode($base64String, true);
9 if ($decoded === false) {
10 throw new Exception("Rentetan Base64 tidak sah");
11 }
12 return $decoded;
13}
14
15// Contoh penggunaan
16$originalText = "Hello, World!";
17$encoded = encodeToBase64($originalText);
18echo "Terkod: " . $encoded . "\n"; // SGVsbG8sIFdvcmxkIQ==
19
20try {
21 $decoded = decodeFromBase64($encoded);
22 echo "Didekod: " . $decoded . "\n"; // Hello, World!
23} catch (Exception $e) {
24 echo "Ralat: " . $e->getMessage() . "\n";
25}
26?>
27
1// Pengekodan/Penyahkodan Base64 C#
2using System;
3using System.Text;
4
5class Base64Example
6{
7 public static string EncodeToBase64(string text)
8 {
9 byte[] textBytes = Encoding.UTF8.GetBytes(text);
10 return Convert.ToBase64String(textBytes);
11 }
12
13 public static string DecodeFromBase64(string base64String)
14 {
15 try
16 {
17 byte[] base64Bytes = Convert.FromBase64String(base64String);
18 return Encoding.UTF8.GetString(base64Bytes);
19 }
20 catch (FormatException)
21 {
22 throw new FormatException("Rentetan Base64 tidak sah");
23 }
24 }
25
26 static void Main()
27 {
28 string originalText = "Hello, World!";
29 string encoded = EncodeToBase64(originalText);
30 Console.WriteLine($"Terkod: {encoded}"); // SGVsbG8sIFdvcmxkIQ==
31
32 try
33 {
34 string decoded = DecodeFromBase64(encoded);
35 Console.WriteLine($"Didekod: {decoded}"); // Hello, World!
36 }
37 catch (FormatException e)
38 {
39 Console.WriteLine($"Ralat: {e.Message}");
40 }
41 }
42}
43
1# Pengekodan/Penyahkodan Base64 Ruby
2require 'base64'
3
4def encode_to_base64(text)
5 Base64.strict_encode64(text)
6end
7
8def decode_from_base64(base64_string)
9 begin
10 Base64.strict_decode64(base64_string)
11 rescue ArgumentError => e
12 raise "Rentetan Base64 tidak sah: #{e.message}"
13 end
14end
15
16# Contoh penggunaan
17original_text = "Hello, World!"
18encoded = encode_to_base64(original_text)
19puts "Terkod: #{encoded}" # SGVsbG8sIFdvcmxkIQ==
20
21begin
22 decoded = decode_from_base64(encoded)
23 puts "Didekod: #{decoded}" # Hello, World!
24rescue StandardError => e
25 puts "Ralat: #{e.message}"
26end
27
1// Pengekodan/Penyahkodan Base64 Go
2package main
3
4import (
5 "encoding/base64"
6 "fmt"
7)
8
9func encodeToBase64(text string) string {
10 return base64.StdEncoding.EncodeToString([]byte(text))
11}
12
13func decodeFromBase64(base64String string) (string, error) {
14 bytes, err := base64.StdEncoding.DecodeString(base64String)
15 if err != nil {
16 return "", fmt.Errorf("rentetan Base64 tidak sah: %v", err)
17 }
18 return string(bytes), nil
19}
20
21func main() {
22 originalText := "Hello, World!"
23 encoded := encodeToBase64(originalText)
24 fmt.Println("Terkod:", encoded) // SGVsbG8sIFdvcmxkIQ==
25
26 decoded, err := decodeFromBase64(encoded)
27 if err != nil {
28 fmt.Println("Ralat:", err)
29 } else {
30 fmt.Println("Didekod:", decoded) // Hello, World!
31 }
32}
33
1// Pengekodan/Penyahkodan Base64 Swift
2import Foundation
3
4func encodeToBase64(_ text: String) -> String? {
5 if let data = text.data(using: .utf8) {
6 return data.base64EncodedString()
7 }
8 return nil
9}
10
11func decodeFromBase64(_ base64String: String) -> String? {
12 if let data = Data(base64Encoded: base64String) {
13 return String(data: data, encoding: .utf8)
14 }
15 return nil
16}
17
18// Contoh penggunaan
19let originalText = "Hello, World!"
20if let encoded = encodeToBase64(originalText) {
21 print("Terkod: \(encoded)") // SGVsbG8sIFdvcmxkIQ==
22
23 if let decoded = decodeFromBase64(encoded) {
24 print("Didekod: \(decoded)") // Hello, World!
25 } else {
26 print("Ralat: Tidak dapat menyahkod rentetan Base64")
27 }
28} else {
29 print("Ralat: Tidak dapat mengekod teks")
30}
31
1' Pengekodan/Penyahkodan Base64 Excel VBA
2' Nota: Ini memerlukan rujukan kepada Microsoft XML, v6.0
3Function EncodeToBase64(text As String) As String
4 Dim xmlObj As Object
5 Set xmlObj = CreateObject("MSXML2.DOMDocument")
6
7 Dim xmlNode As Object
8 Set xmlNode = xmlObj.createElement("b64")
9
10 xmlNode.DataType = "bin.base64"
11 xmlNode.nodeTypedValue = StrConv(text, vbFromUnicode)
12
13 EncodeToBase64 = xmlNode.text
14
15 Set xmlNode = Nothing
16 Set xmlObj = Nothing
17End Function
18
19Function DecodeFromBase64(base64String As String) As String
20 On Error GoTo ErrorHandler
21
22 Dim xmlObj As Object
23 Set xmlObj = CreateObject("MSXML2.DOMDocument")
24
25 Dim xmlNode As Object
26 Set xmlNode = xmlObj.createElement("b64")
27
28 xmlNode.DataType = "bin.base64"
29 xmlNode.text = base64String
30
31 DecodeFromBase64 = StrConv(xmlNode.nodeTypedValue, vbUnicode)
32
33 Set xmlNode = Nothing
34 Set xmlObj = Nothing
35 Exit Function
36
37ErrorHandler:
38 DecodeFromBase64 = "Ralat: Rentetan Base64 tidak sah"
39End Function
40
41' Penggunaan dalam helaian:
42' =EncodeToBase64("Hello, World!")
43' =DecodeFromBase64("SGVsbG8sIFdvcmxkIQ==")
44
1# Pengekodan/Penyahkodan Base64 R
2# Memerlukan pakej 'base64enc'
3# install.packages("base64enc")
4library(base64enc)
5
6encode_to_base64 <- function(text) {
7 # Tukar teks kepada bait mentah, kemudian kodkan
8 text_raw <- charToRaw(text)
9 base64_encoded <- base64encode(text_raw)
10 return(rawToChar(base64_encoded))
11}
12
13decode_from_base64 <- function(base64_string) {
14 tryCatch({
15 # Tukar rentetan base64 kepada mentah, kemudian dekodkan
16 base64_raw <- charToRaw(base64_string)
17 decoded_raw <- base64decode(base64_raw)
18 return(rawToChar(decoded_raw))
19 }, error = function(e) {
20 stop(paste("Rentetan Base64 tidak sah:", e$message))
21 })
22}
23
24# Contoh penggunaan
25original_text <- "Hello, World!"
26encoded <- encode_to_base64(original_text)
27cat("Terkod:", encoded, "\n") # SGVsbG8sIFdvcmxkIQ==
28
29tryCatch({
30 decoded <- decode_from_base64(encoded)
31 cat("Didekod:", decoded, "\n") # Hello, World!
32}, error = function(e) {
33 cat("Ralat:", e$message, "\n")
34})
35
1% Pengekodan/Penyahkodan Base64 MATLAB
2function demo_base64()
3 originalText = 'Hello, World!';
4
5 % Pengekodan
6 encoded = encode_to_base64(originalText);
7 fprintf('Terkod: %s\n', encoded); % SGVsbG8sIFdvcmxkIQ==
8
9 % Penyahkodan
10 try
11 decoded = decode_from_base64(encoded);
12 fprintf('Didekod: %s\n', decoded); % Hello, World!
13 catch e
14 fprintf('Ralat: %s\n', e.message);
15 end
16end
17
18function encoded = encode_to_base64(text)
19 % Tukar teks kepada array uint8 dan kodkan
20 bytes = uint8(text);
21 encoded = base64encode(bytes);
22end
23
24function decoded = decode_from_base64(base64String)
25 try
26 % Dekod rentetan base64 kepada array uint8
27 bytes = base64decode(base64String);
28 decoded = char(bytes);
29 catch
30 error('Rentetan Base64 tidak sah');
31 end
32end
33
1// Pengekodan/Penyahkodan Base64 menggunakan OpenSSL C
2#include <stdio.h>
3#include <string.h>
4#include <openssl/bio.h>
5#include <openssl/evp.h>
6#include <openssl/buffer.h>
7#include <stdint.h>
8
9char* encode_to_base64(const char* input) {
10 BIO *bio, *b64;
11 BUF_MEM *bufferPtr;
12
13 b64 = BIO_new(BIO_f_base64());
14 bio = BIO_new(BIO_s_mem());
15 bio = BIO_push(b64, bio);
16
17 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
18 BIO_write(bio, input, strlen(input));
19 BIO_flush(bio);
20 BIO_get_mem_ptr(bio, &bufferPtr);
21
22 char* result = (char*)malloc(bufferPtr->length + 1);
23 memcpy(result, bufferPtr->data, bufferPtr->length);
24 result[bufferPtr->length] = '\0';
25
26 BIO_free_all(bio);
27
28 return result;
29}
30
31char* decode_from_base64(const char* input) {
32 BIO *bio, *b64;
33 size_t length = strlen(input);
34 char* buffer = (char*)malloc(length);
35
36 b64 = BIO_new(BIO_f_base64());
37 bio = BIO_new_mem_buf(input, -1);
38 bio = BIO_push(b64, bio);
39
40 BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL);
41 int decoded_length = BIO_read(bio, buffer, length);
42
43 if (decoded_length <= 0) {
44 free(buffer);
45 BIO_free_all(bio);
46 return NULL; // Rentetan Base64 tidak sah
47 }
48
49 buffer[decoded_length] = '\0';
50
51 BIO_free_all(bio);
52
53 return buffer;
54}
55
56int main() {
57 const char* original_text = "Hello, World!";
58
59 char* encoded = encode_to_base64(original_text);
60 printf("Terkod: %s\n", encoded); // SGVsbG8sIFdvcmxkIQ==
61
62 char* decoded = decode_from_base64(encoded);
63 if (decoded) {
64 printf("Didekod: %s\n", decoded); // Hello, World!
65 free(decoded);
66 } else {
67 printf("Ralat: Rentetan Base64 tidak sah\n");
68 }
69
70 free(encoded);
71
72 return 0;
73}
74
1// Pengekodan/Penyahkodan Base64 Rust
2use base64::{encode, decode};
3use std::str;
4
5fn encode_to_base64(text: &str) -> String {
6 encode(text)
7}
8
9fn decode_from_base64(base64_string: &str) -> Result<String, String> {
10 match decode(base64_string) {
11 Ok(bytes) => {
12 match str::from_utf8(&bytes) {
13 Ok(text) => Ok(text.to_string()),
14 Err(e) => Err(format!("Urutan UTF-8 tidak sah: {}", e))
15 }
16 },
17 Err(e) => Err(format!("Rentetan Base64 tidak sah: {}", e))
18 }
19}
20
21fn main() {
22 let original_text = "Hello, World!";
23 let encoded = encode_to_base64(original_text);
24 println!("Terkod: {}", encoded); // SGVsbG8sIFdvcmxkIQ==
25
26 match decode_from_base64(&encoded) {
27 Ok(decoded) => println!("Didekod: {}", decoded), // Hello, World!
28 Err(e) => println!("Ralat: {}", e)
29 }
30}
31
Kes-Kes Tepi dan Pertimbangan
Apabila bekerja dengan pengekodan dan penyahkodan Base64, ambil perhatian terhadap pertimbangan penting ini:
-
Karakter Unicode dan Bukan ASCII: Apabila mengekod teks dengan karakter bukan ASCII, pastikan pengekodan karakter yang betul (biasanya UTF-8) sebelum mengekod Base64.
-
Padding: Base64 standard menggunakan padding dengan watak "=" untuk memastikan panjang output adalah kelipatan 4. Beberapa pelaksanaan membenarkan penghapusan padding, yang boleh menyebabkan isu keserasian.
-
Pemisahan Baris: Pelaksanaan Base64 tradisional menyisipkan pemisahan baris (biasanya setiap 76 watak) untuk kebolehan bacaan, tetapi aplikasi moden sering mengabaikannya.
-
Base64 Selamat URL: Base64 standard menggunakan watak "+" dan "/" yang mempunyai makna khas dalam URL. Untuk konteks URL, gunakan Base64 selamat URL yang menggantikan ini dengan "-" dan "_".
-
Ruang Putih: Apabila menyahkod, beberapa pelaksanaan bersifat toleran dan mengabaikan ruang putih, manakala yang lain memerlukan input yang tepat.
-
Peningkatan Saiz: Pengekodan Base64 meningkatkan saiz data sekitar 33% (4 bait output untuk setiap 3 bait input).
-
Prestasi: Pengekodan/penyahkodan Base64 boleh menjadi intensif dari segi pengkomputeran untuk data yang sangat besar. Pertimbangkan pendekatan aliran untuk fail besar.
Rujukan
Maklum Balas
Klik toast maklum balas untuk mula memberi maklum balas tentang alat ini
Alat Berkaitan
Temui lebih banyak alat yang mungkin berguna untuk aliran kerja anda