基于 org.apache.http.client 访问http接口

本文介绍如何使用HttpClient工具包调用HTTP接口,并实现JSON反序列化处理。文章提供了基于连接池的HttpClient配置方法,以及GET、POST请求的具体实现示例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

    背景

    虽然有了dubbo提供的服务接口,但是在项目与项目之间仍旧存在着http访问的接口。那么怎么调用呢?httpClient这个工具包用起来就十分的方便了。一般获取到接口返回的数据时,都需要做json反序列化为对象。可以使用spring-mvc依赖的 com.fasterxml.jackson.core。也可以使用com.alibaba.fastjson

参考资料:

https://www.cnblogs.com/sharpest/p/6406013.html

https://blog.youkuaiyun.com/xiaoxian8023/article/details/49865335

https://blog.youkuaiyun.com/xiaoxian8023/article/details/49619777

引用jar包

  <!-- https://mvnrepository.com/artifact/org.apache.httpcomponents/httpclient -->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.5</version>
        </dependency>

json的jar包,选择其中一个就可以

 <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.6</version>
        </dependency>
       <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.5</version>
        </dependency>

http进行访问的基本步骤

使用HttpClient发送请求、接收响应很简单,一般需要如下几步即可。

1. 创建HttpClient对象。一般都使用连接池来创建

2. 创建请求方法的实例,并指定请求URL。如果需要发送GET请求,创建HttpGet对象;如果需要发送POST请求,创建HttpPost对象。

3. 如果需要发送请求参数,可调用HttpGet、HttpPost共同的setParams(HetpParams params)方法来添加请求参数;对于HttpPost对象而言,也可调用setEntity(HttpEntity entity)方法来设置请求参数。

4. 调用HttpClient对象的execute(HttpUriRequest request)发送请求,该方法返回一个HttpResponse。一般HttpClient是从http连接池里获取。

5. 调用HttpResponse的getAllHeaders()、getHeaders(String name)等方法可获取服务器的响应头;调用HttpResponse的getEntity()方法可获取HttpEntity对象,该对象包装了服务器的响应内容。程序可通过该对象获取服务器的响应内容。

6. 释放连接。无论执行方法是否成功,都必须释放连接。


代码篇

连接池对象

import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.LayeredConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;

import javax.annotation.PostConstruct;
import javax.net.ssl.SSLContext;

/**
 * @author WGP
 * @description 连接池管理对象
 * @date 2018/5/6
 **/
public class HttpConnectionManager {

    PoolingHttpClientConnectionManager cm = null;
    @PostConstruct
    public void init(){
        LayeredConnectionSocketFactory sslsf = null;
        try{
            sslsf = new SSLConnectionSocketFactory(SSLContext.getDefault());

        }catch (Exception e){
            e.printStackTrace();
        }
        Registry<ConnectionSocketFactory> socketFactoryRegistry =
                RegistryBuilder.<ConnectionSocketFactory>create().register("https",sslsf)
                        .register("http",new PlainConnectionSocketFactory()).build();
        cm = new PoolingHttpClientConnectionManager(socketFactoryRegistry);
        cm.setMaxTotal(200);
        cm.setDefaultMaxPerRoute(20);
    }
    public CloseableHttpClient getHttpClient(){
        CloseableHttpClient httpClient = HttpClients.custom().setConnectionManager(cm).build();
        return httpClient;
    }
}

基本使用的方法

public class HttpClient {


    public void postJson() throws Exception {

        String url = "http:/localhost/shop";
        //1、创建http连接
        CloseableHttpClient httpClient = new HttpConnectionManager().getHttpClient();
        CloseableHttpResponse response = null;
        try {
            //2、创建请求对象
            HttpPost httpPost = new HttpPost(url);
//            3、设置请求头及数据
            httpPost.addHeader(HTTP.CONTENT_TYPE,
                    "application/x-www-form-urlencoded");
            String json = "{'ids':['html1','html2'}";
            StringEntity se = new StringEntity(json);
            se.setContentEncoding("UTF-8");
            httpPost.setEntity(se);
            //4、访问http接口
            response = httpClient.execute(httpPost);
            if(response != null && response.getStatusLine().getStatusCode() == 200){
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String res = EntityUtils.toString(entity, "UTF-8");
                    //5、下面可以对res做json反序列化处理
                }
            }
        } catch (Exception e) {
            throw e;
        } finally {
            closeHttp(httpClient,response);
        }
    }

    public void postForm() throws Exception {
        CloseableHttpClient httpClient = new HttpConnectionManager().getHttpClient();
        CloseableHttpResponse response = null;
        try {
            String url = "your/path";
            HttpPost httpPost = new HttpPost(url);
            List<NameValuePair> formParams = new ArrayList<>(8);
            formParams.add(new BasicNameValuePair("username", "yourname"));
            formParams.add(new BasicNameValuePair("password", "pass"));
            UrlEncodedFormEntity uefEntity = new UrlEncodedFormEntity(formParams, "UTF-8");
            httpPost.setEntity(uefEntity);
            response = httpClient.execute(httpPost);
            if(response != null && response.getStatusLine().getStatusCode() == 200){
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String res = EntityUtils.toString(entity, "UTF-8");
                    //下面可以对res做json反序列化处理
                }
            }
        } catch (Exception e) {
            throw e;
        } finally {
            closeHttp(httpClient, response);
        }
    }

    public void getRequest() throws Exception {
        CloseableHttpClient httpClient = new HttpConnectionManager().getHttpClient();
        CloseableHttpResponse response = null;
        try {
            String url = "your/get/path";
            HttpGet httpGet = new HttpGet(url);
            response = httpClient.execute(httpGet);
            if(response != null && response.getStatusLine().getStatusCode() == 200){
                HttpEntity entity = response.getEntity();
                if (entity != null) {
                    String res = EntityUtils.toString(entity, "UTF-8");
                    //下面可以对res做json反序列化处理
                }
            }
        } catch (Exception e) {
            throw e;
        } finally {
            closeHttp(httpClient, response);
        }
    }



    private void closeHttp(CloseableHttpClient httpClient, CloseableHttpResponse response) throws IOException {
        try {
            if (httpClient != null) {
                httpClient.close();
            }
            if (response != null) {
                response.close();
            }
        } catch (IOException ex) {
            throw ex;
        }
    }


}



<think>我们之前使用的SSHD版本是2.15.0,在该版本中,`FileSystemView`和`SshFile`接口确实存在于`org.apache.sshd.server`包中。但是,请注意,在SSHD的后续版本中,这些接口可能已经被移动或重构。 然而,根据问题描述,用户指出在2.15.0版本中`org.apache.sshd.server.SshFile`接口不存在。实际上,在2.15.0版本中,SFTP相关的文件系统接口位于`org.apache.sshd.sftp.server`包下。从SSHD 1.7版本开始,SFTP模块进行了重构,将SFTP服务器的实现移到了单独的模块中,并且包名也有所改变。 因此,我们需要调整之前的代码,使用正确的包和接口。在SSHD 2.15.0中,我们应该使用: - `org.apache.sshd.sftp.server.FileSystemView` - `org.apache.sshd.sftp.server.SftpFileSystemProvider`(可能涉及) - `org.apache.sshd.sftp.server.SftpSubsystemFactory` 同时,文件系统视图的接口为`FileSystemView`,而文件接口为`SftpFile`(位于`org.apache.sshd.sftp.server`包中)。 ### 修正后的实现 #### 1. 依赖确认 确保pom.xml中依赖正确: ```xml <dependency> <groupId>org.apache.sshd</groupId> <artifactId>sshd-sftp</artifactId> <version>2.15.0</version> </dependency> ``` #### 2. 使用正确的接口 - `FileSystemView` 包路径: `org.apache.sshd.sftp.server.FileSystemView` - `SftpFile` 包路径: `org.apache.sshd.sftp.server.SftpFile` #### 3. 调整文件系统视图实现 我们需要实现`FileSystemView`接口,并创建自定义的`SftpFile`实现。 **注意**: 在SSHD 2.15.0中,`FileSystemView`接口有两个方法需要实现: - `getFile(String path)`: 根据路径获取文件 - `getFile(SftpFile baseDir, String path)`: 基于基础目录和相对路径获取文件 #### 4. 自定义SftpFile实现 由于我们不使用真实的文件系统,需要实现`SftpFile`接口。但是,该接口有很多方法,我们可以继承`org.apache.sshd.sftp.server.SftpFileSupport`类来简化实现。 ### 修正后的代码 #### 1. 虚拟文件系统视图 ```java import org.apache.sshd.sftp.server.FileSystemView; import org.apache.sshd.sftp.server.SftpFile; public class VirtualFileSystemView implements FileSystemView { private final String username; private final String rootDir; // 用户根目录,如 "/user1" public VirtualFileSystemView(String username, String rootDir) { this.username = username; this.rootDir = rootDir; } @Override public SftpFile getFile(String path) { // 将路径转换为绝对路径 String absPath = resolveAbsolutePath(path); // 从数据库加载文件信息 VirtualFile meta = fileRepository.findByPathAndOwner(absPath, username); return new VirtualSftpFile(absPath, meta, this); } @Override public SftpFile getFile(SftpFile baseDir, String path) { // 组合基础目录和路径 String fullPath = baseDir.getAbsolutePath() + "/" + path; return getFile(fullPath); } private String resolveAbsolutePath(String path) { // 如果路径是相对路径,则将其解析为绝对路径(相对于用户根目录) if (path.startsWith("/")) { return path; } else { // 注意:这里我们假设当前工作目录是用户根目录 return rootDir + "/" + path; } } } ``` #### 2. 自定义SftpFile实现 ```java import org.apache.sshd.sftp.server.SftpFile; import org.apache.sshd.sftp.server.SftpFileSystemAccessor; import java.io.IOException; import java.nio.file.attribute.FileTime; import java.util.Collection; import java.util.Collections; import java.util.EnumSet; public class VirtualSftpFile implements SftpFile { private final String absolutePath; private final VirtualFile meta; private final VirtualFileSystemView view; public VirtualSftpFile(String absolutePath, VirtualFile meta, VirtualFileSystemView view) { this.absolutePath = absolutePath; this.meta = meta; this.view = view; } @Override public String getAbsolutePath() { return absolutePath; } @Override public String getName() { // 从绝对路径中提取文件名 int idx = absolutePath.lastIndexOf('/'); return (idx >= 0) ? absolutePath.substring(idx + 1) : absolutePath; } @Override public boolean isDirectory() { return meta != null && meta.isDirectory(); } @Override public boolean isFile() { return meta != null && !meta.isDirectory(); } @Override public boolean doesExist() { return meta != null; } @Override public long size() { return meta != null ? meta.getSize() : 0L; } @Override public FileTime getLastModifiedTime() { return FileTime.fromMillis(meta != null ? meta.getLastModified() : 0L); } @Override public FileTime getLastAccessTime() { return getLastModifiedTime(); // 通常相同 } @Override public boolean isReadable() { return doesExist(); // 假设存在即可读 } @Override public boolean isWritable() { return doesExist(); // 假设存在即可写 } @Override public Collection<SftpFile> listSftpFiles() { if (!isDirectory() || !doesExist()) { return Collections.emptyList(); } // 查询数据库,获取该目录下的所有文件和子目录 List<VirtualFile> children = fileRepository.listByParentPathAndOwner(absolutePath, view.getUsername()); return children.stream() .map(childMeta -> { String childPath = absolutePath + "/" + childMeta.getFilename(); return new VirtualSftpFile(childPath, childMeta, view); }) .collect(Collectors.toList()); } // 其他方法需要实现,例如创建、删除、移动等,但根据需求,我们可能只需要部分功能 // 对于不支持的操作,可以抛出UnsupportedOperationException @Override public void create() throws IOException { throw new UnsupportedOperationException("Create operation not supported"); } // ... 其他方法类似,根据需求实现或抛出异常 } ``` #### 3. 文件系统工厂调整 ```java import org.apache.sshd.sftp.server.FileSystemFactory; import org.apache.sshd.sftp.server.FileSystemView; import org.apache.sshd.server.session.ServerSession; public class VirtualFileSystemFactory implements FileSystemFactory { @Autowired private UserRepository userRepository; @Override public FileSystemView createFileSystemView(ServerSession session) { String username = session.getUsername(); VirtualUser user = userRepository.findById(username).orElseThrow(); return new VirtualFileSystemView(username, user.getVirtualRoot()); } } ``` #### 4. 配置SFTP服务器 ```java import org.apache.sshd.sftp.server.SftpSubsystemFactory; @Configuration public class SftpServerConfig { @Value("${sftp.port:2222}") private int port; @Bean public SshServer sshServer() throws Exception { SshServer sshd = SshServer.setUpDefaultServer(); sshd.setPort(port); sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider()); sshd.setPasswordAuthenticator((username, password, session) -> userRepository.findByUsernameAndPassword(username, password) != null ); sshd.setFileSystemFactory(new VirtualFileSystemFactory()); sshd.setSubsystemFactories(Collections.singletonList( new SftpSubsystemFactory() )); sshd.start(); return sshd; } } ``` ### 注意事项 1. **数据库设计调整**:在`VirtualFile`表中,我们需要存储文件的绝对路径(如`/user1/docs/file.txt`)或者相对路径(但需要根据父目录查询)。同时,需要存储文件名、是否为目录、大小、修改时间、所有者等信息。 2. **性能考虑**:频繁的数据库查询可能会影响性能,可以考虑使用缓存(如Caffeine)来缓存目录结构。 3. **事务和并发**:当多个SFTP客户端同时操作时,需要确保数据库操作的原子性和一致性。 4. **错误处理**:在自定义的`SftpFile`实现中,对于不支持的操作,应抛出`UnsupportedOperationException`。 5. **文件内容存储**:如果要求存储文件内容(而不仅仅是元数据),则需要将文件内容存储在数据库(BLOB)或外部存储(如对象存储)中,并在`SftpFile`中实现读写方法。 ### 总结 通过以上实现,我们基于SSHD-SFTP 2.15.0版本,使用MySQL作为后端存储,实现了虚拟文件系统,并为每个用户设置了自定义的虚拟根目录。用户通过SFTP客户端连接后,只能看到自己的虚拟根目录下的文件结构,所有操作都映射到数据库的虚拟文件记录上。 §§相关问题§§: 1. 如何修改代码以支持文件内容存储(将文件内容存储在MySQL的BLOB字段中)? 2. 如何优化虚拟文件系统的性能(例如使用缓存)? 3. 如何实现虚拟文件系统的权限控制(如读、写、执行权限)? 4. 如何处理并发访问同一文件时的冲突?</think>### 修正实现:使用正确的 SSHD 2.15.0 SFTP API 在 SSHD 2.15.0 版本中,`FileSystemView` 和 `SshFile` 接口已被重构为新的 API。正确实现需要以下关键调整: #### 1. 使用正确的接口 - **FileSystemFactory** → `org.apache.sshd.sftp.server.SftpSubsystemFactory.FileSystemFactory` - **SftpFile** → `org.apache.sshd.sftp.server.SftpFileSystemProvider.SftpFile` #### 2. 核心实现代码 ##### 虚拟文件系统视图 ```java import org.apache.sshd.sftp.server.FileHandle; import org.apache.sshd.sftp.server.SftpFileSystemAccessor; import org.apache.sshd.sftp.server.SftpFileSystemProvider; import org.apache.sshd.sftp.server.SftpSubsystemFactory; public class VirtualFileSystemView implements SftpFileSystemProvider.SftpFile { private final Path path; private final VirtualFile meta; private final String username; public VirtualFileSystemView(Path path, String username, VirtualFile meta) { this.path = path; this.username = username; this.meta = meta; } @Override public Path getPath() { return path; } @Override public boolean isDirectory() { return meta != null && meta.isDirectory(); } @Override public boolean isRegularFile() { return meta != null && !meta.isDirectory(); } @Override public long size() { return meta != null ? meta.getSize() : 0; } @Override public FileTime getLastModifiedTime() { return FileTime.fromMillis(meta != null ? meta.getLastModified() : 0); } @Override public List<Path> listDirectory(SftpFileSystemAccessor accessor) throws IOException { if (!isDirectory()) throw new NotDirectoryException(path.toString()); List<VirtualFile> children = fileRepository.findByParentPathAndOwner( path.toString(), username ); return children.stream() .map(child -> Paths.get(path.toString(), child.getName())) .collect(Collectors.toList()); } @Override public InputStream read(SftpFileSystemAccessor accessor, long offset) throws IOException { if (isDirectory()) throw new AccessDeniedException("Cannot read directory"); return new ByteArrayInputStream(meta.getContent()); } // 其他方法实现类似... } ``` ##### 文件系统工厂 ```java import org.apache.sshd.sftp.server.SftpSubsystemFactory; public class VirtualFileSystemFactory implements SftpSubsystemFactory.FileSystemFactory { @Autowired private UserRepository userRepository; @Override public SftpFileSystemProvider createFileSystemProvider( SftpSubsystem subsystem, Path defaultDir) { return new VirtualFileSystemProvider(subsystem); } } public class VirtualFileSystemProvider extends SftpFileSystemProvider { private final SftpSubsystem subsystem; public VirtualFileSystemProvider(SftpSubsystem subsystem) { this.subsystem = subsystem; } @Override public SftpFileSystemProvider.SftpFile getFile(Path path) { String username = subsystem.getSession().getUsername(); VirtualFile meta = fileRepository.findByPathAndOwner( path.toString(), username ); return new VirtualFileSystemView(path, username, meta); } } ``` ##### SFTP 服务器配置 ```java @Configuration public class SftpServerConfig { @Value("${sftp.port:2222}") private int port; @Bean public SshServer sshServer() throws IOException { SshServer sshd = SshServer.setUpDefaultServer(); sshd.setPort(port); // 1. 主机密钥配置 sshd.setKeyPairProvider(new SimpleGeneratorHostKeyProvider()); // 2. 用户认证 sshd.setPasswordAuthenticator((username, password, session) -> userRepository.findByUsernameAndPassword(username, password) != null ); // 3. 配置SFTP子系统 SftpSubsystemFactory sftpFactory = new SftpSubsystemFactory(); sftpFactory.setFileSystemFactory(new VirtualFileSystemFactory()); sshd.setSubsystemFactories(Collections.singletonList(sftpFactory)); sshd.start(); return sshd; } } ``` #### 3. 数据库模型调整 ```java @Entity public class VirtualFile { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; private String path; // 完整虚拟路径 private String name; // 文件名 private boolean isDirectory; private long size; private long lastModified; @Lob private byte[] content; // 文件内容 private String owner; // 所属用户 // Getters & Setters } ``` #### 4. 关键实现说明 1. **正确使用 SSHD 2.15.0 API**: - 使用 `SftpSubsystemFactory.FileSystemFactory` 替代旧版 `FileSystemFactory` - 实现 `SftpFileSystemProvider.SftpFile` 接口替代旧版 `SshFile` 2. **用户根目录隔离**: - 在 `getFile()` 方法中通过 `subsystem.getSession().getUsername()` 获取当前用户 - 所有路径查询自动添加用户前缀 3. **文件内容存储**: - 使用 `@Lob byte[] content` 存储文件内容 - `read()` 方法直接返回内容的输入流 4. **目录列表实现**: - `listDirectory()` 查询数据库获取子文件列表 - 使用 `Paths.get(parent, child)` 构建完整路径 #### 5. 文件上传处理 ```java @Override public OutputStream write( SftpFileSystemAccessor accessor, long offset, OpenOption... options) throws IOException { if (isDirectory()) throw new AccessDeniedException("Cannot write to directory"); return new FilterOutputStream(new ByteArrayOutputStream()) { @Override public void close() throws IOException { super.close(); ByteArrayOutputStream bos = (ByteArrayOutputStream) out; byte[] content = bos.toByteArray(); // 更新数据库中的文件内容 VirtualFile file = fileRepository.findByPathAndOwner( path.toString(), username ); file.setContent(content); file.setSize(content.length); file.setLastModified(System.currentTimeMillis()); fileRepository.save(file); } }; } ``` ### 完整工作流程 ```mermaid sequenceDiagram SFTP Client->>+SSHD Server: 连接请求(user1/pass1) SSHD Server->>+MySQL: 验证用户凭证 MySQL-->>-SSHD Server: 验证结果 SSHD Server->>VirtualFileSystem: 创建/user1根目录视图 SFTP Client->>VirtualFileSystem: put file.txt VirtualFileSystem->>MySQL: 创建/更新文件记录 MySQL-->>VirtualFileSystem: 操作结果 VirtualFileSystem-->>SFTP Client: 上传成功 ``` ### 性能优化建议 1. **大文件处理**: ```java @Override public OutputStream write(...) { return new BufferedOutputStream(new FilterOutputStream(...) { // 分块写入数据库 }); } ``` 2. **缓存机制**: ```java @Cacheable(value = "virtualFiles", key = "#path + #username") public VirtualFile findByPathAndOwner(String path, String username) { // 数据库查询 } ``` 3. **连接池配置**: ```yaml spring: datasource: hikari: maximum-pool-size: 20 connection-timeout: 30000 ```
评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值