Rumah pembangunan bahagian belakang Tutorial C#.Net 分享WebApi2 文件图片上传与下载功能实例

分享WebApi2 文件图片上传与下载功能实例

May 31, 2017 pm 01:59 PM
api web

这篇文章主要介绍了WebApi2 文件图片上传与下载功能,需要的朋友可以参考下

Asp.Net Framework webapi2 文件上传与下载 前端界面采用Ajax的方式执行

一、项目结构

1.App_Start配置了跨域访问,以免请求时候因跨域问题不能提交。具体的跨域配置方式如下,了解的朋友请自行略过。

跨域配置:NewGet安装dll Microsofg.AspNet.Cors

然后在App_Start 文件夹下的WebApiConfig.cs中写入跨域配置代码。

public static class WebApiConfig
  {
    public static void Register(HttpConfiguration config)
    {
      // Web API configuration and services
      // Web API routes
      config.MapHttpAttributeRoutes();
      // Web API configuration and services
      //跨域配置 //need reference from nuget
      config.EnableCors(new EnableCorsAttribute("*", "*", "*"));
      config.Routes.MapHttpRoute(
        name: "DefaultApi",
        routeTemplate: "api/{controller}/{id}",
        defaults: new { id = RouteParameter.Optional }
      );
      //if config the global filter input there need not write the attributes
      //config.Filters.Add(new App.WebApi.Filters.ExceptionAttribute_DG());
    }
  }
Salin selepas log masuk

跨域就算完成了,请自行测试。

2.新建两个控制器,一个PicturesController.cs,一个FilesController.cs当然图片也是文件,这里图片和文件以不同的方式处理的,因为图片的方式文件上传没有成功,所以另寻他路,如果在座的有更好的方式,请不吝赐教!

二、项目代码

1.我们先说图片上传、下载控制器接口,这里其实没什么好说的,就一个Get获取文件,参数是文件全名;Post上传文件;直接上代码。


using QX_Frame.App.WebApi;
using QX_Frame.FilesCenter.Helper;
using QX_Frame.Helper_DG;
using QX_Frame.Helper_DG.Extends;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
/**
 * author:qixiao
 * create:2017-5-26 16:54:46
 * */
namespace QX_Frame.FilesCenter.Controllers
{
  public class PicturesController : WebApiControllerBase
  {
    //Get : api/Pictures
    public HttpResponseMessage Get(string fileName)
    {
      HttpResponseMessage result = null;
      DirectoryInfo directoryInfo = new DirectoryInfo(IO_Helper_DG.RootPath_MVC + @"Files/Pictures");
      FileInfo foundFileInfo = directoryInfo.GetFiles().Where(x => x.Name == fileName).FirstOrDefault();
      if (foundFileInfo != null)
      {
        FileStream fs = new FileStream(foundFileInfo.FullName, FileMode.Open);
        result = new HttpResponseMessage(HttpStatusCode.OK);
        result.Content = new StreamContent(fs);
        result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        result.Content.Headers.ContentDisposition.FileName = foundFileInfo.Name;
      }
      else
      {
        result = new HttpResponseMessage(HttpStatusCode.NotFound);
      }
      return result;
    }
    //POST : api/Pictures
    public async Task<IHttpActionResult> Post()
    {
      if (!Request.Content.IsMimeMultipartContent())
      {
        throw new Exception_DG("unsupported media type", 2005);
      }
      string root = IO_Helper_DG.RootPath_MVC;
      IO_Helper_DG.CreateDirectoryIfNotExist(root + "/temp");
      var provider = new MultipartFormDataStreamProvider(root + "/temp");
      // Read the form data. 
      await Request.Content.ReadAsMultipartAsync(provider);
      List<string> fileNameList = new List<string>();
      StringBuilder sb = new StringBuilder();
      long fileTotalSize = 0;
      int fileIndex = 1;
      // This illustrates how to get the file names.
      foreach (MultipartFileData file in provider.FileData)
      {
        //new folder
        string newRoot = root + @"Files/Pictures";
        IO_Helper_DG.CreateDirectoryIfNotExist(newRoot);
        if (File.Exists(file.LocalFileName))
        {
          //new fileName
          string fileName = file.Headers.ContentDisposition.FileName.Substring(1, file.Headers.ContentDisposition.FileName.Length - 2);
          string newFileName = Guid.NewGuid() + "." + fileName.Split(&#39;.&#39;)[1];
          string newFullFileName = newRoot + "/" + newFileName;
          fileNameList.Add($"Files/Pictures/{newFileName}");
          FileInfo fileInfo = new FileInfo(file.LocalFileName);
          fileTotalSize += fileInfo.Length;
          sb.Append($" #{fileIndex} Uploaded file: {newFileName} ({ fileInfo.Length} bytes)");
          fileIndex++;
          File.Move(file.LocalFileName, newFullFileName);
          Trace.WriteLine("1 file copied , filePath=" + newFullFileName);
        }
      }
      return Json(Return_Helper.Success_Msg_Data_DCount_HttpCode($"{fileNameList.Count} file(s) /{fileTotalSize} bytes uploaded successfully!   Details -> {sb.ToString()}", fileNameList, fileNameList.Count));
    }
  }
}
Salin selepas log masuk

里面可能有部分代码在Helper帮助类里面写的,其实也仅仅是获取服务器根路径和如果判断文件夹不存在则创建目录,这两个代码的实现如下:


 public static string RootPath_MVC
     {
       get { return System.Web.HttpContext.Current.Server.MapPath("~"); }
     }
//create Directory
    public static bool CreateDirectoryIfNotExist(string filePath)
    {
      if (!Directory.Exists(filePath))
      {
        Directory.CreateDirectory(filePath);
      }
      return true;
    }
Salin selepas log masuk

2.文件上传下载接口和图片大同小异。


using QX_Frame.App.WebApi;
using QX_Frame.FilesCenter.Helper;
using QX_Frame.Helper_DG;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Threading.Tasks;
using System.Web;
using System.Web.Http;
/**
 * author:qixiao
 * create:2017-5-26 16:54:46
 * */
namespace QX_Frame.FilesCenter.Controllers
{
  public class FilesController : WebApiControllerBase
  {
    //Get : api/Files
    public HttpResponseMessage Get(string fileName)
    {
      HttpResponseMessage result = null;
      DirectoryInfo directoryInfo = new DirectoryInfo(IO_Helper_DG.RootPath_MVC + @"Files/Files");
      FileInfo foundFileInfo = directoryInfo.GetFiles().Where(x => x.Name == fileName).FirstOrDefault();
      if (foundFileInfo != null)
      {
        FileStream fs = new FileStream(foundFileInfo.FullName, FileMode.Open);
        result = new HttpResponseMessage(HttpStatusCode.OK);
        result.Content = new StreamContent(fs);
        result.Content.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
        result.Content.Headers.ContentDisposition = new ContentDispositionHeaderValue("attachment");
        result.Content.Headers.ContentDisposition.FileName = foundFileInfo.Name;
      }
      else
      {
        result = new HttpResponseMessage(HttpStatusCode.NotFound);
      }
      return result;
    }
    //POST : api/Files
    public async Task<IHttpActionResult> Post()
    {
      //get server root physical path
      string root = IO_Helper_DG.RootPath_MVC;
      //new folder
      string newRoot = root + @"Files/Files/";
      //check path is exist if not create it
      IO_Helper_DG.CreateDirectoryIfNotExist(newRoot);
      List<string> fileNameList = new List<string>();
      StringBuilder sb = new StringBuilder();
      long fileTotalSize = 0;
      int fileIndex = 1;
      //get files from request
      HttpFileCollection files = HttpContext.Current.Request.Files;
      await Task.Run(() =>
      {
        foreach (var f in files.AllKeys)
        {
          HttpPostedFile file = files[f];
          if (!string.IsNullOrEmpty(file.FileName))
          {
            string fileLocalFullName = newRoot + file.FileName;
            file.SaveAs(fileLocalFullName);
            fileNameList.Add($"Files/Files/{file.FileName}");
            FileInfo fileInfo = new FileInfo(fileLocalFullName);
            fileTotalSize += fileInfo.Length;
            sb.Append($" #{fileIndex} Uploaded file: {file.FileName} ({ fileInfo.Length} bytes)");
            fileIndex++;
            Trace.WriteLine("1 file copied , filePath=" + fileLocalFullName);
          }
        }
      });
      return Json(Return_Helper.Success_Msg_Data_DCount_HttpCode($"{fileNameList.Count} file(s) /{fileTotalSize} bytes uploaded successfully!   Details -> {sb.ToString()}", fileNameList, fileNameList.Count));
    }
  }
}
Salin selepas log masuk

实现了上述两个控制器代码以后,我们需要前端代码来调试对接,代码如下所示。


<!doctype>
<head>
  <script src="jquery-3.2.0.min.js"></script>
  <!--<script src="jquery-1.11.1.js"></script>-->
  <!--<script src="ajaxfileupload.js"></script>-->
  <script>
    $(document).ready(function () {
      var appDomain = "http://localhost:3997/";
      $("#btn_fileUpload").click(function () {
        /**
         * 用ajax方式上传文件  -----------
         * */
        //-------asp.net webapi fileUpload
        //
        var formData = new FormData($("#uploadForm")[0]);
        $.ajax({
          url: appDomain + &#39;api/Files&#39;,
          type: &#39;POST&#39;,
          data: formData,
          async: false,
          cache: false,
          contentType: false,
          processData: false,
          success: function (data) {
            console.log(JSON.stringify(data));
          },
          error: function (data) {
            console.log(JSON.stringify(data));
          }
        });
        //----end asp.net webapi fileUpload
        //----.net core webapi fileUpload
        // var fileUpload = $("#files").get(0);
        // var files = fileUpload.files;
        // var data = new FormData();
        // for (var i = 0; i < files.length; i++) {
        //    data.append(files[i].name, files[i]);
        // }
        // $.ajax({
        //   type: "POST",
        //   url: appDomain+&#39;api/Files&#39;,
        //   contentType: false,
        //   processData: false,
        //   data: data,
        //   success: function (data) {
        //     console.log(JSON.stringify(data));
        //   },
        //   error: function () {
        //     console.log(JSON.stringify(data));
        //   }
        // });
        //--------end net core webapi fileUpload
        /**
         * ajaxfileupload.js 方式上传文件
         * */
        // $.ajaxFileUpload({
        //   type: &#39;post&#39;,
        //   url: appDomain + &#39;api/Files&#39;,
        //   secureuri: false,
        //   fileElementId: &#39;files&#39;,
        //   success: function (data) {
        //     console.log(JSON.stringify(data));
        //   },
        //   error: function () {
        //     console.log(JSON.stringify(data));
        //   }
        // });
      });
      //end click
    })
  </script>
</head>
<title></title>
<body>
  <article>
    <header>
      <h2>article-form</h2>
    </header>
    <p>
      <form action="/" method="post" id="uploadForm" enctype="multipart/form-data">
        <input type="file" id="files" name="files" placeholder="file" multiple>file-multiple属性可以选择多项<br><br>
        <input type="button" id="btn_fileUpload" value="fileUpload">
      </form>
    </p>
  </article>
</body>
Salin selepas log masuk

至此,我们的功能已全部实现,下面我们来测试一下:

可见,文件上传成功,按预期格式返回!

下面我们测试单图片上传->

然后我们按返回的地址进行访问图片地址。

发现并无任何压力!

下面测试多图片上传->

完美~

至此,我们已经实现了WebApi2文件和图片上传,下载的全部功能。

这里需要注意一下Web.config的配置上传文件支持的总大小,我这里配置的是最大支持的文件大小为1MB

<requestFiltering>
  <requestLimits maxAllowedContentLength="1048576" />
</requestFiltering>
  <system.webServer>
   <handlers>
    <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
    <remove name="OPTIONSVerbHandler" />
    <remove name="TRACEVerbHandler" />
    <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
   </handlers>
   <security>
   <requestFiltering>
    <requestLimits maxAllowedContentLength="1048576" /><!--1MB-->
    </requestFiltering>
  </security>
  </system.webServer>
Salin selepas log masuk

【相关推荐】

1. ASP.NET免费视频教程

2. 详细介绍ASP.NET MVC--路由

3. 详细介绍ASP.NET MVC--控制器(controller)

4. 详细介绍ASP.NET MVC--视图

Atas ialah kandungan terperinci 分享WebApi2 文件图片上传与下载功能实例. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan Laman Web ini
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn

Alat AI Hot

Undresser.AI Undress

Undresser.AI Undress

Apl berkuasa AI untuk mencipta foto bogel yang realistik

AI Clothes Remover

AI Clothes Remover

Alat AI dalam talian untuk mengeluarkan pakaian daripada foto.

Undress AI Tool

Undress AI Tool

Gambar buka pakaian secara percuma

Clothoff.io

Clothoff.io

Penyingkiran pakaian AI

AI Hentai Generator

AI Hentai Generator

Menjana ai hentai secara percuma.

Artikel Panas

R.E.P.O. Kristal tenaga dijelaskan dan apa yang mereka lakukan (kristal kuning)
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Tetapan grafik terbaik
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. Cara Memperbaiki Audio Jika anda tidak dapat mendengar sesiapa
4 minggu yang lalu By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25: Cara Membuka Segala -galanya Di Myrise
1 bulan yang lalu By 尊渡假赌尊渡假赌尊渡假赌

Alat panas

Notepad++7.3.1

Notepad++7.3.1

Editor kod yang mudah digunakan dan percuma

SublimeText3 versi Cina

SublimeText3 versi Cina

Versi Cina, sangat mudah digunakan

Hantar Studio 13.0.1

Hantar Studio 13.0.1

Persekitaran pembangunan bersepadu PHP yang berkuasa

Dreamweaver CS6

Dreamweaver CS6

Alat pembangunan web visual

SublimeText3 versi Mac

SublimeText3 versi Mac

Perisian penyuntingan kod peringkat Tuhan (SublimeText3)

Bagaimana untuk memuat turun episod drama pendek Hongguo Bagaimana untuk memuat turun episod drama pendek Hongguo Mar 11, 2024 pm 09:16 PM

Hongguo Short Play bukan sahaja platform untuk menonton drama pendek, tetapi juga khazanah kandungan yang kaya, termasuk novel dan kandungan menarik yang lain. Ini tidak dinafikan satu kejutan besar bagi ramai pengguna yang suka membaca. Walau bagaimanapun, ramai pengguna masih tidak tahu cara memuat turun dan menonton novel ini dalam Hongguo Short Play Dalam berikut, editor tapak ini akan memberikan anda langkah memuat turun terperinci, dengan harapan dapat membantu semua Rakan Kongsi yang memerlukan. Bagaimana untuk memuat turun dan menonton drama pendek Hongguo Jawapan: [Main pendek Hongguo] - [Buku audio] - [Artikel] - [Muat turun]. Langkah-langkah khusus: 1. Mula-mula buka perisian Drama Pendek Hongguo, masukkan halaman utama dan klik butang [Dengar Buku] di bahagian atas halaman 2. Kemudian pada halaman novel kita boleh melihat banyak kandungan artikel, di sini

Apakah yang perlu saya lakukan jika saya memuat turun kertas dinding orang lain selepas log masuk ke akaun lain pada mesin kertas dinding? Apakah yang perlu saya lakukan jika saya memuat turun kertas dinding orang lain selepas log masuk ke akaun lain pada mesin kertas dinding? Mar 19, 2024 pm 02:00 PM

Apabila anda log masuk ke akaun stim orang lain pada komputer anda, dan akaun orang lain itu kebetulan mempunyai perisian kertas dinding, stim akan memuat turun kertas dinding yang dilanggan ke akaun orang lain secara automatik selepas bertukar kembali ke akaun anda sendiri Pengguna boleh menyelesaikan masalah ini dengan mematikan penyegerakan awan wap. Apa yang perlu dilakukan jika enjin kertas dinding memuat turun kertas dinding orang lain selepas log masuk ke akaun lain 1. Log masuk ke akaun stim anda sendiri, cari penyegerakan awan dalam tetapan, dan matikan penyegerakan awan stim. 2. Log masuk ke akaun Steam orang lain yang anda log masuk sebelum ini, buka Bengkel Kreatif Kertas Dinding, cari kandungan langganan, dan kemudian batalkan semua langganan. (Sekiranya anda tidak dapat mencari kertas dinding pada masa hadapan, anda boleh mengumpulnya dahulu dan kemudian membatalkan langganan) 3. Tukar semula ke wap anda sendiri

Bagaimana untuk memuat turun pautan bermula dengan 115://? Muat turun pengenalan kaedah Bagaimana untuk memuat turun pautan bermula dengan 115://? Muat turun pengenalan kaedah Mar 14, 2024 am 11:58 AM

Baru-baru ini, ramai pengguna bertanya kepada editor, bagaimana untuk memuat turun pautan bermula dengan 115://? Jika anda ingin memuat turun pautan bermula dengan 115://, anda perlu menggunakan pelayar 115 Selepas anda memuat turun pelayar 115, mari lihat tutorial muat turun yang disusun oleh editor di bawah. Pengenalan kepada cara memuat turun pautan bermula dengan 115:// 1. Log masuk ke 115.com, muat turun dan pasang pelayar 115. 2. Masukkan: chrome://extensions/ dalam bar alamat pelayar 115, masukkan pusat sambungan, cari Tampermonkey dan pasang pemalam yang sepadan. 3. Masukkan dalam bar alamat pelayar 115: Skrip Grease Monkey: https://greasyfork.org/en/

Pengenalan kepada cara memuat turun dan memasang permainan superpeople Pengenalan kepada cara memuat turun dan memasang permainan superpeople Mar 30, 2024 pm 04:01 PM

Permainan superpeople boleh dimuat turun melalui klien wap Saiz permainan ini ialah kira-kira 28G Ia biasanya mengambil masa satu setengah jam untuk memuat turun dan memasang Berikut ialah tutorial muat turun dan pemasangan khusus untuk anda. Kaedah baharu untuk memohon ujian tertutup global 1) Cari "SUPERPEOPLE" dalam kedai Steam (muat turun klien stim) 2) Klik "Minta akses kepada ujian tertutup SUPERPEOPLE" di bahagian bawah halaman kedai "SUPERPEOPLE" 3) Selepas mengklik butang minta butang akses, Permainan "SUPERPEOPLECBT" boleh disahkan dalam perpustakaan Steam 4) Klik butang pasang dalam "SUPERPEOPLECBT" dan muat turun

Bagaimana untuk memuat turun cakera rangkaian Quark ke setempat? Bagaimana untuk menyimpan fail yang dimuat turun dari Quark Network Disk kembali ke komputer tempatan Bagaimana untuk memuat turun cakera rangkaian Quark ke setempat? Bagaimana untuk menyimpan fail yang dimuat turun dari Quark Network Disk kembali ke komputer tempatan Mar 13, 2024 pm 08:31 PM

Ramai pengguna perlu memuat turun fail apabila menggunakan Cakera Rangkaian Quark, tetapi kami ingin menyimpannya secara tempatan, jadi bagaimana untuk menyediakannya? Biarkan tapak ini memperkenalkan kepada pengguna secara terperinci cara menyimpan fail yang dimuat turun dari Quark Network Disk kembali ke komputer tempatan. Cara menyimpan fail yang dimuat turun dari cakera rangkaian Quark kembali ke komputer tempatan anda 1. Buka Quark, log masuk ke akaun anda dan klik ikon senarai. 2. Selepas mengklik ikon, pilih cakera rangkaian. 3. Selepas memasukkan Quark Network Disk, klik My Files. 4. Selepas memasukkan Fail Saya, pilih fail yang anda ingin muat turun dan klik ikon tiga titik. 5. Semak fail yang anda ingin muat turun dan klik Muat turun.

Bagaimana untuk memuat turun foobar2000? -Cara menggunakan foobar2000 Bagaimana untuk memuat turun foobar2000? -Cara menggunakan foobar2000 Mar 18, 2024 am 10:58 AM

foobar2000 ialah perisian yang boleh mendengar sumber muzik pada bila-bila masa Ia membawakan anda semua jenis muzik dengan kualiti bunyi tanpa kehilangan Versi pemain muzik yang dipertingkatkan membolehkan anda mendapatkan pengalaman muzik yang lebih komprehensif dan selesa mainkan audio lanjutan pada komputer Peranti dipindahkan ke telefon mudah alih untuk memberikan pengalaman main balik muzik yang lebih mudah dan cekap Reka bentuk antara muka adalah ringkas, jelas dan mudah digunakan Ia menggunakan gaya reka bentuk minimalis tanpa terlalu banyak hiasan dan operasi yang menyusahkan untuk bermula dengan cepat. Ia juga menyokong pelbagai kulit dan Tema, memperibadikan tetapan mengikut pilihan anda sendiri, dan mencipta pemain muzik eksklusif yang menyokong main balik berbilang format audio. Ia juga menyokong fungsi perolehan audio untuk melaraskan kelantangan kepada keadaan pendengaran anda sendiri untuk mengelakkan kerosakan pendengaran yang disebabkan oleh kelantangan yang berlebihan. Seterusnya, izinkan saya membantu anda

Di mana untuk memuat turun fail dari Quark Network Disk_Cara memuat turun Quark Network Disk ke kawasan setempat dan berkongsi Di mana untuk memuat turun fail dari Quark Network Disk_Cara memuat turun Quark Network Disk ke kawasan setempat dan berkongsi Mar 21, 2024 pm 03:57 PM

Sebagai alat cakera rangkaian yang mudah dan praktikal, Quark boleh membantu pengguna mendapatkan sumber kegemaran mereka dengan mudah Bagaimana jika anda ingin memuat turun fail secara tempatan? Biar editor memberitahu anda sekarang, mari belajar bersama! Cara memuat turun Cakera Rangkaian Quark ke perkongsian setempat 1. Mula-mula buka perisian Quark, masukkan halaman utama dan klik [Ikon Awan] di sebelah kanan bawah 2. Kemudian pada halaman Cakera Rangkaian Quark, kami klik fungsi [Dokumen]; 3. Kemudian pergi ke halaman dokumen, pilih fail yang anda ingin muat turun, dan klik [ikon tiga titik] 4. Selepas klik terakhir, kami klik [Muat turun] dalam kotak dialog pop timbul;

Bagaimana untuk memuat turun lagu-lagu Beilehu children Bagaimana untuk memuat turun lagu-lagu Beilehu children Mar 28, 2024 am 11:10 AM

Sebagai iringan yang sangat diperlukan untuk tumbesaran kanak-kanak, lagu-lagu kanak-kanak Beilehu telah memenangi kasih sayang ibu bapa dan kanak-kanak yang tidak terkira banyaknya dengan melodi yang ceria, gambar yang jelas dan kandungan yang menghiburkan dan mendidik. Bagi membolehkan bayi menikmati kegembiraan yang dibawakan oleh lagu kanak-kanak pada bila-bila masa dan di mana sahaja, ramai ibu bapa berharap untuk memuat turun lagu kanak-kanak Beilehu ke telefon bimbit atau tablet mereka supaya mereka boleh mendengar anak-anak mereka pada bila-bila masa, tetapi bagaimana untuk menyimpan lagu kanak-kanak Beilehu. ? Pada telefon mudah alih anda, tutorial ini akan membawakan anda pengenalan terperinci. Pengguna yang belum memahaminya boleh datang dan membaca bersama artikel ini untuk mengetahui lebih lanjut. Beilehu Nursery Rhymes Muat Turun Tutorial Pelbagai Gambar Lagu Kanak-kanak: Buka perisian dan pilih lagu kanak-kanak yang anda ingin muat turun Editor mengambil "Lagu Kanak-Kanak Klasik" sebagai contoh 2. Klik butang "Muat turun" di bawah bintang lagu kanak-kanak.

See all articles