详解与FTP服务器相关操作

目录

什么是FTP服务器

搭建FTP服务器相关

​编辑

Unity中与FTP相关的类

上传文件到FTP服务器

使用FTP服务器上传文件的关键点

开始上传

从FTP服务器下载文件到客户端

使用FTP下载文件的关键点

开始下载

关于FTP服务器的其他操作

将文件的上传,下载,删除等其他操作使用面向对象的思想用单例模式进行封装

测试FTPMgr模块的功能

什么是FTP服务器

  • 基本概念:FTP 是文件传输协议(File Transfer Protocol)的缩写,FTP 服务器就是遵循该协议,专门用来存储和管理文件,并为用户提供文件上传和下载服务的服务器。用户可以通过 FTP 客户端软件连接到 FTP 服务器,进行文件的传输操作。
  • 功能特点
    • 文件传输:支持文件的上传和下载,用户可以将本地计算机上的文件上传到 FTP 服务器,也可以从 FTP 服务器下载所需的文件到本地。
    • 用户管理:可以设置不同的用户账户,并为每个用户分配不同的权限,如读取、写入、删除等权限,从而实现对文件访问的精细控制。
    • 目录管理:允许创建、删除、重命名目录,以及在目录之间移动文件等操作,方便对文件进行组织和管理。

搭建FTP服务器相关

 #region 知识点一 搭建FTP服务器的几种方式//1.使用别人做好的FTP服务器软件 (学习阶段建议使用)//2.自己编写FTP服务器应用程序,基于FTP的工作原理,用Socket中TCP通信来进行编程(工作后由后端程序员来做) //3.将电脑搭建为FTP文件共享服务器 (工作后由后端程序员来做)//第2,3点我们前端程序主要做了解  //一般在工作中不会由我们来完成这部分工作#endregion   #region 知识点二 使用别人做好的FTP服务器软件来搭建FTP服务器//下载Serv-U等FTP服务器软件   //在想要作为FTP服务器的电脑上运行之  //1.创建域 直接不停下一步即可   //2.使用单向加密  //3.创建用于上传下载的FTP 账号和密码#endregion  #region 总结  //在实际商业项目开发当中,如果需要用FTP来进行文件传输      //那么FTP服务器的解决方案都是由后端程序员来完成的    //不管它使用哪种方式来搭建FTP服务器  //只要能正常上传下载内容并且保证安全性即可#endregion

我们使用Serv-U服务器作为我们的FTP服务器

Unity中与FTP相关的类

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using UnityEngine;public class Lesson18 : MonoBehaviour
{// Start is called before the first frame updatevoid Start(){#region 知识点一 NetWorkCredential类//命名空间:System.Net//NetWorkCredential通信凭证类//用于在FTP文件传输时,设置账号密码NetworkCredential n = new NetworkCredential("DamnF", "DamnF123");#endregion#region 知识点二 FtpWebRequest类//命名空间:System.Net//Ftp文件传输协议客户端操作类//主要用于:上传,下载,删除服务器上的文件//重要方法//1.Create 创建新的WebRequest,用于进行相关的Ftp相关操作FtpWebRequest req =FtpWebRequest.Create(new Uri("ftp://127.0.0.1/Test.txt"))as FtpWebRequest;//2.Abort 如果正在进行文件传输,用此方法可以终止文件传输req.Abort();//3.GetRequestStream 获取用于上传的流Stream s= req.GetRequestStream();//4.GetResponse 返回Ftp服务器响应//FtpWebResponse res= req.GetResponse()as FtpWebResponse;//重要成员//1.Credentials 通信凭证,设置为NetWorkCredential对象req.Credentials = n;//2.KeepAlive bool值,当完成请求时是否关闭到Ftp服务器的控制连接(默认为true,不关闭);req.KeepAlive = false;//3.Method 操作命令设置// WebRequestMethods.Ftp类中的操作命令属性// DeleteFile  删除文件// DownloadFile  下载文件// ListDirectory  获取文件简短列表// ListDirectoryDetails  获取文件详细列表// MakeDirectory  创建目录// RemoveDirectory  删除目录// UploadFile  上传文件req.Method = WebRequestMethods.Ftp.DownloadFile;//4.UseBinary 是否使用2进制传输req.UseBinary = true;//5.RenameTo  重命名req.RenameTo = "myTest.txt";#endregion#region 知识点三 FtpWebResponse类//命名空间:System.Net//它是用于封装Ftp服务器对请求的响应//它提供操作状态和从服务器下载数据//我们可以通过FtpWebRequest对象中的GetResponse()方法获取//当使用完毕时,要使用Close释放//通过它真正获取服务器的内容FtpWebResponse res = req.GetResponse() as FtpWebResponse;//重要方法//1.Close:释放所有资源res.Close();//2.GetResponseStream:返回从Ftp下载数据的流Stream stream= res.GetResponseStream();//重要成员//1.ContentLength:接收到数据的长度print(res.ContentLength);//2.ContentType:接收到数据的类型print(res.ContentType);//3.StatusCode:Ftp服务器下发的最新状态码print(res.StatusCode);//4.StatusDescription:Ftp服务器下发的状态代码的文本print(res.StatusDescription);//5.BannerMessage:登录前建立连接时Ftp服务器下发的消息print(res.BannerMessage);//6.ExitMessage:Ftp会话结束时服务器发送的消息print(res.ExitMessage);//7.LastModified:Ftp服务器上的文件的上次修改日期和时间print(res.LastModified);#endregion#region 总结//通过C#提供的3个类//我们就可以完成客户端向Ftp服务器//操作文件的需求,比如//上传,下载,删除文件#endregion }// Update is called once per framevoid Update(){}
}

上传文件到FTP服务器

使用FTP服务器上传文件的关键点

        //1.通信凭证
        //  进行FTP连接时操作时的账号和密码
        //2.操作命令WebRequestMethod.Ftp
        // 设置你想要进行的Ftp操作
        //3.文件流相关FileStream和Stream
        //  上传和下载时都会使用到文件流
        //4.保证ftp服务器已经开启
        // 并且能够正确访问

开始上传

 try{//1.创建一个Ftp连接FtpWebRequest req = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/pic.png")) as FtpWebRequest;//2.设置通信凭证(如果不支持匿名,就必须设置这一步)//将代理相关设置置空 避免服务器同时有http相关服务冲突req.Proxy = null;NetworkCredential n = new NetworkCredential("DamnF", "DamnF123");req.Credentials = n;//请求完毕后,是否关闭控制连接,如果想要关闭,需要设置falsereq.KeepAlive = false;//3.设置操作命令req.Method = WebRequestMethods.Ftp.UploadFile;//设置操作命令为上传文件//4.指定传输类型req.UseBinary = true;//5.得到用于上传的流对象Stream upLoadStream = req.GetRequestStream();//6.开始上传using (FileStream file = File.OpenRead(Application.streamingAssetsPath + "/test.png")){//我们可以一点一点的把这个文件中的字节数组读取出来 然后存到上传流中byte[] bytes = new byte[1024];//返回值是真正从文件中读了多少个字节int contentLength = file.Read(bytes, 0, bytes.Length);//不停的去读取文件中的字节流 除非读完了 不然一直读 并且写入到上传流中while (contentLength != 0){//写入上传流中upLoadStream.Write(bytes, 0, contentLength);//写完了继续读取contentLength = file.Read(bytes, 0, bytes.Length);}//出了循环就证明写完了file.Close();upLoadStream.Close();//上传完毕print("上传完毕");}}catch (Exception e){print("上传失败 出错了" + e.Message);}

从FTP服务器下载文件到客户端

使用FTP下载文件的关键点

        //1.通信凭证
        // 进行FTP文件操作时需要的账号和密码
        //2.操作命令WebRequestMethods.Ftp
        // 设置你想要进行的Ftp操作
        //3.文件流相关的FileStream和Stream
        // 上传和下载时都需要使用的文件流
        // 下载文件流使用FtpWebResponse类获取
        //4.保证Ftp服务器已开启
        // 并且能够正常访问

开始下载

try{//1.创建一个Ftp连接//这里和上传不同,上传的文件名是自己定的 下载的文件名必须是资源服务器里有的FtpWebRequest req = FtpWebRequest.Create(new Uri("ftp://127.0.0.1/文本格式.jpg")) as FtpWebRequest;//2.设置一个通信凭证(如果不支持匿名,就必须设置这一步)//请求完毕后 是否关闭控制连接,如果要进行多次操作 可以设置falseNetworkCredential n = new NetworkCredential("DamnF", "DamnF123");req.Credentials = n;req.KeepAlive = false;//3.设置操作命令req.Method = WebRequestMethods.Ftp.DownloadFile;//4.指定传输类型req.UseBinary = true;req.Proxy = null;//5.得到用于上传的流对象//相当于把请求发送给ftp服务器,就会携带我们想要的信息返回FtpWebResponse res = req.GetResponse()as FtpWebResponse;//这就是下载流Stream DownLoadStream = res.GetResponseStream ();//6.开始上传print(Application.persistentDataPath);using (FileStream file = File.Create(Application.persistentDataPath + "/DamnF112233.jpg")){byte[] bytes = new byte[1024];int contentLength = file.Read(bytes, 0, bytes.Length);while (contentLength != 0){file.Write(bytes, 0, contentLength);contentLength = file.Read(bytes, 0, bytes.Length);}file.Close();DownLoadStream.Close();print("下载完毕");}}catch (Exception e){print("下载出错了" + e.Message);}

关于FTP服务器的其他操作

 //除了上传和下载,还有其他操作
        //比如:
        //1.删除文件
        //2.获取文件大小
        //3.创建文件夹
        //4.获取文件列表
        //等等

将文件的上传,下载,删除等其他操作使用面向对象的思想用单例模式进行封装

using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.Events;public class FtpMgr
{private static FtpMgr instance=new FtpMgr();public static FtpMgr Instance => instance;//ftp服务器地址private string FTP_PATH = "ftp://127.0.0.1";//用户名密码private string USER_NAME = "DamnF";private string PASSWORD = "DamnF123";public async void UpLoadFile(string fileName,string localPath,UnityAction action=null){await Task.Run(() =>{try{//通过一个线程执行这里边的逻辑,不会影响主线程//1.创建ftp连接FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH+fileName)) as FtpWebRequest;//2.设置通信凭证(如果不支持匿名,就必须设置这一步)//将代理相关设置置空 避免服务器同时有http相关服务冲突req.Proxy = null;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;//请求完毕后,是否关闭控制连接,如果想要关闭,需要设置falsereq.KeepAlive = false;//3.设置操作命令req.Method = WebRequestMethods.Ftp.UploadFile;//4.指定传输类型req.UseBinary = true;//5.得到用于上传的流对象Stream upLoadStream = req.GetRequestStream();//6.开始上传using (FileStream file = File.OpenRead(localPath)){//我们可以一点一点的把这个文件中的字节数组读取出来 然后存到上传流中byte[] bytes = new byte[1024];//返回值是真正从文件中读了多少个字节int contentLength = file.Read(bytes, 0, bytes.Length);//不停的去读取文件中的字节流 除非读完了 不然一直读 并且写入到上传流中while (contentLength != 0){//写入上传流中upLoadStream.Write(bytes, 0, contentLength);//写完了继续读取contentLength = file.Read(bytes, 0, bytes.Length);}//出了循环就证明写完了file.Close();upLoadStream.Close();}//上传完毕Debug.Log("上传完毕");}catch (Exception e){Debug.Log("上传出错了" + e.Message);}});//上传结束后,你想在外部做的事情action?.Invoke();}public async void DownLoadFile(string fileName,string localName,UnityAction action=null){await Task.Run(() =>{try{FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH + fileName)) as FtpWebRequest;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;req.KeepAlive = false;req.Proxy = null;req.UseBinary = true;req.Method = WebRequestMethods.Ftp.DownloadFile;FtpWebResponse res = req.GetResponse() as FtpWebResponse;Stream DownLoadStream = res.GetResponseStream();using (FileStream file = File.Create(localName)){byte[] bytes = new byte[1024];int contentLength = file.Read(bytes, 0, bytes.Length);while (contentLength != 0){file.Write(bytes, 0, contentLength);contentLength = file.Read(bytes, 0, bytes.Length);}file.Close();DownLoadStream.Close();Debug.Log("下载完毕");}res.Close();}catch (Exception e){Debug.Log("下载出错了" + e.Message);}});action?.Invoke();}public async void DeleteFile(string fileName,UnityAction<bool> action=null){await Task.Run(() =>{try{FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH + fileName)) as FtpWebRequest;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;req.Proxy = null;req.KeepAlive = false;req.UseBinary = true;req.Method = WebRequestMethods.Ftp.DeleteFile;FtpWebResponse res = req.GetResponse() as FtpWebResponse;res.Close();action?.Invoke(true);}catch (Exception e){Debug.Log("删除文件出错"+e.Message);action?.Invoke(false);}});}public async void GetFileSize(string fileName,UnityAction<long>action=null){try{await Task.Run(() =>{FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH + fileName)) as FtpWebRequest;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;req.KeepAlive = false;req.Proxy = null;req.UseBinary = true;req.Method = WebRequestMethods.Ftp.GetFileSize;FtpWebResponse res = req.GetResponse() as FtpWebResponse;action?.Invoke(res.ContentLength);res.Close();});}catch (Exception e){Debug.Log("得到文件大小出错" + e.Message);}}public async void CreateDirectory(string DirectoryName,UnityAction<bool>action=null){try{await Task.Run(() =>{FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH + DirectoryName)) as FtpWebRequest;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;req.Proxy = null;req.KeepAlive = false;req.UseBinary = true;req.Method = WebRequestMethods.Ftp.MakeDirectory;FtpWebResponse res = req.GetResponse() as FtpWebResponse;res.Close();action?.Invoke(true);});}catch (Exception e){Debug.Log("文件夹创建失败" + e.Message);action?.Invoke(false);}}public async void GetFileList(string directoryName,UnityAction<List <string>>action=null){try{await Task.Run(() =>{FtpWebRequest req = FtpWebRequest.Create(new Uri(FTP_PATH + directoryName)) as FtpWebRequest;NetworkCredential n = new NetworkCredential(USER_NAME, PASSWORD);req.Credentials = n;req.Proxy = null;req.KeepAlive = false;req.UseBinary = true;req.Method = WebRequestMethods.Ftp.ListDirectory;FtpWebResponse res = req.GetResponse() as FtpWebResponse;//把下载的信息流转化为StreamReader对象,方便我们一行一行的读取信息StreamReader streamReader = new StreamReader(res.GetResponseStream());//用于存储文件名的列表List<string> nameStrs = new List<string>();//一行一行的读取信息string line= streamReader.ReadLine();while(line!=null){nameStrs.Add(line);line = streamReader.ReadLine();}action?.Invoke(nameStrs);res.Close();});}catch (Exception e){Debug.Log("文件列表返回失败" + e.Message);action?.Invoke(null);}}
}

测试FTPMgr模块的功能

   FtpMgr.Instance.UpLoadFile("/DamnFPic.png", Application.streamingAssetsPath + "/test.png", () =>{print("上传成功,调用委托函数");});print("测试测试");print(Application.persistentDataPath);FtpMgr.Instance.DownLoadFile("/文本格式.jpg", Application .persistentDataPath +"/zhangdeshuai1234.jpg", () => {print("下载成功,调用委托函数");});print("测试测试");
 //1.删除文件FtpMgr.Instance.DeleteFile("/Pic.png", (result) =>{print(result ? "删除完毕" : "删除失败");});//2.获取文件大小FtpMgr.Instance.GetFileSize("/文本格式.jpg", (result) => {print("文件大小是:" + result);});//3.创建文件夹FtpMgr.Instance.CreateDirectory("/DamnF", (result) => {print(result ? "创建成功" : "创建失败");});//4.获取文件列表FtpMgr.Instance.GetFileList("", (list) => {if(list==null){print("返回文件列表失败");return;}else{for (int i = 0; i < list.Count ; i++){print(list[i]);}}});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/901692.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Day92 | 灵神 | 二叉树 路径总和

Day92 | 灵神 | 二叉树 路径总和 112.路径总和 112. 路径总和 - 力扣&#xff08;LeetCode&#xff09; 思路&#xff1a; 1.递归函数意义 如果在根节点为t的树中可以找到长度为target的路径就返回true&#xff0c;找不到就返回false 2.参数和返回值 bool tra(TreeNode …

探索鸿蒙应用开发:ArkTS应用执行入口揭秘

# 探索鸿蒙应用开发&#xff1a;ArkTS应用执行入口揭秘 在鸿蒙应用开发的领域中&#xff0c;ArkTS作为声明式开发语言&#xff0c;为开发者们带来了便捷与高效。对于刚接触鸿蒙开发的小伙伴来说&#xff0c;搞清楚ArkTS应用程序的执行入口是迈向成功开发的关键一步。今天&…

【Web API系列】Web Shared Storage API之WorkletSharedStorage深度解析与实践指南

前言 在现代Web开发领域&#xff0c;数据存储与隐私保护的矛盾始终存在。传统存储方案如LocalStorage和Cookies面临着日益严格的安全限制&#xff0c;而跨域数据共享的需求却在持续增长。正是在这样的背景下&#xff0c;Web Shared Storage API应运而生&#xff0c;其核心组件…

探索鸿蒙沉浸式:打造无界交互体验

一、鸿蒙沉浸式简介 在鸿蒙系统中&#xff0c;沉浸式是一种极具特色的设计理念&#xff0c;它致力于让用户在使用应用时能够全身心投入到内容本身&#xff0c;而尽可能减少被系统界面元素的干扰。通常来说&#xff0c;就是将应用的内容区巧妙地延伸到状态栏和导航栏所在的界面…

机器学习03——K近邻

K近邻算法学习笔记 一、算法简介 K近邻算法&#xff08;K - Nearest Neighbors&#xff0c;简称KNN&#xff09;是一种简单而有效的分类和回归算法。它的核心思想是“近朱者赤&#xff0c;近墨者黑”&#xff0c;即一个数据点的类别或值可以通过其周围最近的K个邻居来判断。K…

序列化 反序列化实例

在Python中&#xff0c; pickle 模块常用于实现对象的序列化和反序列化&#xff0c;以下是一个简单的实例&#xff1a; import pickle # 定义一个类 class Person: def __init__(self, name, age): self.name name self.age age # 创建一个Person对象 person Person("…

代码随想录算法训练营第十九天

LeetCode题目: 77. 组合216. 组合总和 III17. 电话号码的字母组合2537. 统计好子数组的数目(每日一题)516. 最长回文子序列1039. 多边形三角剖分的最低得分543. 二叉树的直径124. 二叉树中的最大路径和2246. 相邻字符不同的最长路径 其他: 今日总结 往期打卡 77. 组合 跳转: 7…

存算分离看场景

计算机行业是唯一一个比时装行业概念更多的行业。概念频出&#xff0c;最慢的话半年一定出一个&#xff0c;短的话半个月就能看到新的名词和技术甚至是概念。 存算分离的概念 我第一次听到存算分离时候还是从Hadoop上听到的。然后就去问什么是存算分离。听了讲解以后&#xf…

MCP协议,.Net 使用示例

服务器端示例 基础服务器 以下是一个基础的 MCP 服务器示例&#xff0c;它使用标准输入输出&#xff08;stdio&#xff09;作为传输方式&#xff0c;并实现了一个简单的回显工具&#xff1a; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.H…

智能语音处理+1.5使用PocketSphinxshinx实现语音转文本(100%教会)

欢迎来到智能语音处理系列的最后一篇文章&#xff0c;到这里,基本上语音处理是没问题了. 第一篇:智能语音处理1.1下载需要的库(100%实现)-CSDN博客 第二篇:智能语音识别1.2用SAPI实现文本转语音(100%教会)-CSDN博客 第三篇:智能语音处理1.3用SpeechLib实现文本转语音(100%教会)…

Kubernetes 节点摘除指南

目录 一、安全摘除节点的标准流程 1. 确认节点名称及状态 2. 标记节点为不可调度 3. 排空&#xff08;Drain&#xff09;节点 4. 删除节点 二、验证节点是否成功摘除 1. 检查节点列表 2. 检查节点详细信息 3. 验证 Pod 状态 三、彻底清理节点&#xff08;可选&#xf…

信息安全管理与评估2021年国赛正式卷答案截图以及十套国赛卷

2021年全国职业院校技能大赛高职组 “信息安全管理与评估”赛项 任务书1 赛项时间 共计X小时。 赛项信息 赛项内容 竞赛阶段 任务阶段 竞赛任务 竞赛时间 分值 第一阶段 平台搭建与安全设备配置防护 任务1 网络平台搭建 任务2 网络安全设备配置与防护 第二…

3D语义地图中的全局路径规划!iPPD:基于3D语义地图的指令引导路径规划视觉语言导航

作者&#xff1a; Zehao Wang, Mingxiao Li, Minye Wu, Marie-Francine Moens, Tinne Tuytelaars 单位&#xff1a;鲁汶大学电气工程系&#xff0c;鲁汶大学计算机科学系 论文标题&#xff1a; Instruction-guided path planning with 3D semantic maps for vision-language …

《AI大模型应知应会100篇》第20篇:大模型伦理准则与监管趋势

第20篇&#xff1a;大模型伦理准则与监管趋势 摘要 随着人工智能&#xff08;AI&#xff09;技术的飞速发展&#xff0c;尤其是大模型&#xff08;如GPT、PaLM等&#xff09;在自然语言处理、图像生成等领域的广泛应用&#xff0c;AI伦理问题和监管挑战日益凸显。本文将梳理当…

【Ai】dify:Linux环境安装 dify 详细步骤

一、什么是dify Dify 是一个 开源的大语言模型(LLM)应用开发平台,旨在帮助开发者快速构建基于 AI 的应用程序,例如智能对话助手、知识库问答、内容生成工具等。它提供了可视化的流程编排、模型集成、数据管理等功能,降低了开发门槛,支持快速迭代和部署。 核心功能与特点…

CentOS 操作系统下搭建 tsung性能测试环境

写在前面 为何这么安装,实际就是这么做的,这是经过好几次实践得出的经验总结。 这为了让大家更清楚的知道怎么安装 tsung性能测试环境,按步照搬的安装即可。 步骤 1、 下载软件安装包 CentOS-6.0-x86_64-bin-DVD1.iso jdk-6u4-linux-x64-rpm.bin erlang: otp_src_1…

Vulkanised

Vulkanised 1. About VulkanisedReferences The Premier Vulkan Developer Conference premier /ˈpremiə(r)/ n. 总理&#xff1b;(尤用于报章等) 首相&#xff1b;(加拿大的) 省总理&#xff1b;地区总理 adj. 第一的&#xff1b;首要的&#xff1b;最著名的&#xff1b;最…

C++之 动态数组

一、新建一个动态数组 数组名和下标操作符[]的组合可以被替换成一个指向该数组的基地址的指针和对应的指针运算&#xff1a; int a[20]; int *x a; 指针变量 x 指向数组 a 的地址&#xff0c; a[0] 和 *x 都代表数组的第一个元素。 于是&#xff0c;根据指针运算原则&…

ubuntu1804服务器开启ftp,局域网共享特定文件给匿名用户

要在 Ubuntu 18.04 上设置一个 FTP 服务器&#xff0c;满足以下要求&#xff1a; 允许匿名登录&#xff08;无需账号密码&#xff09;。指定分享特定目录下的文件。只允许只读下载。 可以使用 vsftpd&#xff08;Very Secure FTP Daemon&#xff09;来实现。以下是详细步骤&a…

mcp和API区别

MCP&#xff08;Model Context Protocol&#xff0c;模型上下文协议&#xff09;与传统API&#xff08;Application Programming Interface&#xff0c;应用程序编程接口&#xff09;在技术架构、集成方式和应用场景等方面存在显著差异&#xff0c;以下是主要区别的总结&#x…