GDAL VSI文件扩展(virtual_file_systems扩展)

需求: 需要支持 远程打开 FTP或者网络上的 tif或者img文件, 但是GDAL不能依赖curl编译, 无法直接使用 /vsicurl/ftp://192.168.0.22/a.tif 这种路径丢给GDALOpen去打开tif文件;

解决方案:

GDAL在支持ftp或者网络数据集得时候定义了一套虚拟文件系统接口, 所有打开本地或者远程资源都是用 VSIFilesystemHandler 和 VSIVirtualHandle.这两个接口操作.

经过研究发现只需要实现以上两个接口并注册进GDAL里面他就会自动来根据操作打开文件,然后去读取文件内容.

需要自行实现一个 VSIFilesystemHandler 和 VSIVirtualHandle.

其中VSIFilesystemHandler需要实现以下接口

//需要实现以下接口
class CPL_DLL VSIFilesystemHandler {



    virtual VSIVirtualHandle *Open( const char *pszFilename,
                                    const char *pszAccess,
                                    bool bSetError,
                                    CSLConstList papszOptions ) = 0;
    virtual int Stat( const char *pszFilename, VSIStatBufL *pStatBuf, int nFlags) = 0;
}

VSIVirtualHandle需要是实现以下接口

class CPL_DLL VSIVirtualHandle {
  public:
    virtual int       Seek( vsi_l_offset nOffset, int nWhence ) = 0;
    virtual vsi_l_offset Tell() = 0;
    virtual size_t    Read( void *pBuffer, size_t nSize, size_t nCount ) = 0;
    virtual size_t    Write( const void *pBuffer, size_t nSize,size_t nCount)=0;
    virtual int       Eof() = 0;
    virtual int       Close() = 0;
}

最后在打开数据之前还需要注册一下:

VSIFileManager::InstallHandler(file.FullPath().c_str(), new cpl::GsVSIFilesystemHandler(file.FullPath().c_str(), pGDB));
m_pDS = (GDALDataset *)GDALOpen(file.FullPath().c_str(), GA_ReadOnly);

本文实现远程ftp文件访问并用GDAL解析, 具体代码如下:

#include "pch.h"
#include "vsifileimp.h"

using namespace GeoStar::Kernel;
using namespace GeoStar::Utility;


//FTP 实现的坑, FTP同一文件一个流没读完,下一个流不起作用, (IMG文件gdal内部需要打开和关闭两次)
//FTP  文件流不支持seek ,只能下载向前读, 这在tif文件得时候是可以得, 但是在img不行,
//FTP  不要视图多线程, 因为只能打开一个文件流,
//FTP  镶嵌数据集支持ftp是个玩具,需要上对象化存储

int cpl::GsVSIVirtualHandle::Eof()
{
	return beof;
}
//return 0 on success or -1 one failure.
int cpl::GsVSIVirtualHandle::Seek(vsi_l_offset nOffset, int nWhence)
{
	if (nOffset < 0 || nOffset > filesize)
		return -1;
	if (nWhence == SEEK_SET)
	{
		curOffset = nOffset;
	}
	else if (nWhence == SEEK_CUR)
	{
		curOffset = curOffset + nOffset;
	}
	else
	{
		curOffset = filesize + nOffset;
	}
	beof = false;
	return 0;
}
//*@return 0 on success or -1 on failure.
vsi_l_offset cpl::GsVSIVirtualHandle::Tell()
{
	return curOffset;
}
//
//size_t cpl::GsVSIVirtualHandle::Read(void * pBufferIn, size_t nSize, size_t nCount)
//{
//	if (0 == nSize * nCount)
//		return 0;
//	unsigned char*pbufferhead = (unsigned char*)pBufferIn;
//	unsigned long long needsize = nCount * nSize;
//
//	if (needsize + curOffset <= loadsize)
//	{
//		GsInputStreamPtr read = m_TmpFile->OpenRead();
//		read->Seek(curOffset, GsStreamSeekOrigin::eSet);
//		int n = read->RawRead((unsigned char*)pbufferhead, needsize);
//		curOffset += n;
//		return n;
//	}
//	unsigned long long AllReturnSize = 0;
//	long long Infilesize = loadsize - curOffset;
//	if (Infilesize > 0)
//	{
//		GsInputStreamPtr read = m_TmpFile->OpenRead();
//		int tmpfileread  = read->RawRead((unsigned char*)pbufferhead, needsize);
//		pbufferhead += tmpfileread;
//		needsize -= tmpfileread;
//		curOffset += tmpfileread;
//		AllReturnSize += tmpfileread;
//	}
//	unsigned long long outfilesize = fabs(Infilesize);
//	unsigned long long needdownsize = outfilesize + needsize;
//	int buffsize = 1024;
//	GsGrowByteBuffer buff;
//	buff.Allocate(buffsize);
//	{auto write = m_TmpFile->OpenWrite();
//	unsigned long long downCount = 0;
//	//直接放文件
//	if (outfilesize < buffsize)
//		buffsize = outfilesize;
//	while (outfilesize > 0)
//	{
//		int n = this->FTPRead(buff.BufferHead(), 1, buffsize);
//		if (n <= 0)
//			break;
//		downCount += n;
//		outfilesize -= n;
//		if (outfilesize < buffsize)
//			buffsize = outfilesize;
//		loadsize += n;
//		buff.Allocate(n);
//		write->WriteBuffer(&buff);
//	}
//
//	buffsize = 1024;
//	int tmpwritecount = 0;
//	if (needsize < buffsize)
//		buffsize = needsize;
//	while (needsize > 0)
//	{
//		int n = this->FTPRead(pbufferhead, 1, buffsize);
//		if (n <= 0)
//			break;
//
//		buff.Allocate(n);
//		buff.Copy(pbufferhead, n);
//		tmpwritecount += write->WriteBuffer(&buff);
//		pbufferhead += n;
//		needsize -= n;
//		AllReturnSize += n;
//		curOffset += n;
//		loadsize += n;
//		if (needsize < buffsize)
//			buffsize = needsize;
//	}
//	write->Flush();
//	write->Close();
//	}
//	//curOffset += AllReturnSize;
//	return AllReturnSize;
//	
//}
//#define BLOCKSIZE 1024*16
//size_t cpl::GsVSIVirtualHandle::Read(void * pBufferIn, size_t nSize, size_t nCount)
//{
//	if (0 == nSize * nCount)
//		return 0;
//	unsigned char*pbufferhead = (unsigned char*)pBufferIn;
//	long long needsize = nCount * nSize;
//
//	if (needsize + curOffset <= loadsize)
//	{
//		FILE* file = fopen(m_tmpFilePath.c_str(), "r");
//		fseek(file, curOffset, 0);
//		int n = fread((unsigned char*)pbufferhead, nSize, nCount, file);
//		curOffset += n;
//		fclose(file);
//		return n;
//	}
//	long long AllReturnSize = 0;
//	long long Infilesize = loadsize - curOffset;
//	if (Infilesize > 0)
//	{
//		FILE* file = fopen(m_tmpFilePath.c_str(), "r");
//		fseek(file, curOffset, 0);
//		int tmpfileread = fread((unsigned char*)pbufferhead, 1, needsize, file);
//		fclose(file);
//		pbufferhead += tmpfileread;
//		needsize -= tmpfileread;
//		curOffset += tmpfileread;
//		AllReturnSize += tmpfileread;
//	}
//	long long outfilesize = fabs(Infilesize);
//	long long needdownsize = outfilesize + needsize;
//	int buffsize = BLOCKSIZE;
//	GsGrowByteBuffer buff;
//	int tmp = 0;
//	buff.Allocate(buffsize);
//	{
//		FILE* file = fopen(m_tmpFilePath.c_str(), "a+");
//		long long downCount = 0;
//		//直接放文件
//		//if (outfilesize < buffsize)
//		//buffsize = outfilesize;
//		while (outfilesize > 0)
//		{
//			int n = this->FTPRead(buff.BufferHead(), 1, buffsize);
//			if (n <= 0)
//				break;
//
//			if (outfilesize < n)
//			{
//				buff.Allocate(outfilesize);
//			}
//			else
//			{
//				buff.Allocate(n);
//			}	
//			tmp = fwrite(buff.BufferHead(), 1, buff.BufferSize(), file);
//			downCount += n;
//			outfilesize -= n;
//			loadsize += n;
//		
//		}
//
//		buffsize = BLOCKSIZE;
//		int tmpwritecount = 0;
//		buff.Allocate(buffsize);
//		//if (needsize < buffsize)
//		//	buffsize = needsize;
//		while (needsize > 0)
//		{
//			int n = this->FTPRead(buff.BufferHead(), 1, buffsize);
//			if (n <= 0)
//				break;
//
//			buff.Allocate(n);
//			tmpwritecount += fwrite(buff.BufferHead(), 1, buff.BufferSize(), file);
//			
//			if (needsize < n)
//			{
//				buffsize = needsize;
//				memcpy(pbufferhead, buff.BufferHead(), needsize);
//				//pbufferhead += n;
//				//needsize -= n;
//				AllReturnSize += needsize;
//				curOffset += needsize;
//				loadsize += n;
//			}
//			else
//			{
//				memcpy(pbufferhead, buff.BufferHead(), n);
//				pbufferhead += n;
//				needsize -= n;
//				AllReturnSize += n;
//				curOffset += n;
//				loadsize += n;
//			}
//
//
//		}
//		fflush(file);
//		fclose(file);
//	}
//
//	return AllReturnSize;
//
//}

#define BLOCKSIZE 1024
#define BLOCKSIZE_C 1024
#define BLOCKSIZE_RC BLOCKSIZE*BLOCKSIZE * BLOCKSIZE_C
//分段读取目前是不成功,  在img文件的ftp文件中, 这里不停得打开关闭文件存在问题
size_t cpl::GsVSIVirtualHandle::Read2(void * pBufferIn, size_t nSize, size_t nCount)
{
	if (0 == nSize * nCount)
		return 0;
	unsigned char*pbufferhead = (unsigned char*)pBufferIn;
	unsigned long long needsize = nCount * nSize;
	if (needsize + curOffset > filesize)
		return 0;
	if (needsize + curOffset <= loadsize)
	{
		FILE* file = fopen(m_tmpFilePath.c_str(), "rb");
		fseek(file, curOffset, 0);
		int n = fread((unsigned char*)pbufferhead, nSize, nCount, file);
		curOffset += n;
		fclose(file);
		return n;
	}
	unsigned long long AllReturnSize = 0;
	long long Infilesize = (loadsize > curOffset) ? (loadsize - curOffset) : (-1 * (curOffset - loadsize));
	if (Infilesize > 0)
	{
		FILE* file = fopen(m_tmpFilePath.c_str(), "rb");
		fseek(file, curOffset, 0);
		int tmpfileread = fread((unsigned char*)pbufferhead, 1, needsize, file);
		fclose(file);
		pbufferhead += tmpfileread;
		needsize -= tmpfileread;
		curOffset += tmpfileread;
		AllReturnSize += tmpfileread;
	}
	unsigned long long outfilesize = fabs(Infilesize);
	unsigned long long needdownsize = outfilesize + needsize;
	int buffsize = BLOCKSIZE;
	GsGrowByteBuffer buff;
	int tmp = 0;
	buff.Allocate(buffsize);
	{
		FILE* file = fopen(m_tmpFilePath.c_str(), "ab+");
		unsigned long long downCount = 0;
		//直接放文件
		if (outfilesize < buffsize)
			buffsize = outfilesize;
		while (outfilesize > 0)
		{
			int n = this->FTPRead(buff.BufferHead(), 1, buffsize);
			if (n <= 0)
				break;
			downCount += n;
			outfilesize -= n;
			loadsize += n;
			if (outfilesize < n)
				buffsize = outfilesize;
			buff.Allocate(n);
			tmp = fwrite(buff.BufferHead(), 1, buff.BufferSize(), file);

		}

		buffsize = BLOCKSIZE;
		int tmpwritecount = 0;
		buff.Allocate(buffsize);
		if (needsize < buffsize)
			buffsize = needsize;
		while (needsize > 0)
		{
			int n = this->FTPRead(pbufferhead, 1, buffsize);
			if (n <= 0)
				break;

			needsize -= n;
			AllReturnSize += n;
			curOffset += n;
			loadsize += n;
			buff.Allocate(n);
			buff.Copy(pbufferhead, n);
			tmpwritecount += fwrite(buff.BufferHead(), 1, buff.BufferSize(), file);
			if (needsize < n)
				buffsize = needsize;
			pbufferhead += n;
		}
		fflush(file);
		fclose(file);
	}

	return AllReturnSize < 0 ? -1 : AllReturnSize;
}
size_t cpl::GsVSIVirtualHandle::ReadLocal(void * pBufferIn, size_t nSize, size_t nCount)
{
	if (0 == nSize * nCount)
		return 0;
	unsigned char*pbufferhead = (unsigned char*)pBufferIn;
	unsigned long long needsize = nCount * nSize;

	if (needsize + curOffset <= loadsize)
	{
		//if (!rfile)
		FILE*	rfile = fopen(m_tmpFilePath.c_str(), "rb");
		int f = fseek(rfile, curOffset, 0);
		int n = fread((unsigned char*)pbufferhead, nSize, nCount, rfile);
		curOffset += n * nSize;
		//防止句柄耗尽,及时关闭
		fclose(rfile);
		return n;
	}
	return 0;
}
size_t cpl::GsVSIVirtualHandle::Read2Local(void * pBuffer, size_t nSize, size_t nCount)
{
	unsigned long long needsize = nCount * nSize;
	{
		GsGrowByteBuffer buff;
		long long tmpwritecount = 0;
		buff.Allocate(BLOCKSIZE);
		//if (!wfile)
		FILE*	wfile = fopen(m_tmpFilePath.c_str(), "ab+");
		long long needDownsize = (long long)curOffset - (long long)loadsize + (long long)needsize;
		if (filesize < BLOCKSIZE_RC)
			needDownsize = filesize;
		else if (filesize > BLOCKSIZE_RC)
		{
			if (needDownsize < BLOCKSIZE_RC)
				needDownsize = BLOCKSIZE_RC;
			//if (needDownsize + loadsize > filesize)
			//{
			//	needDownsize = (long long) filesize - (long long)loadsize;
			//}
		}
		while (needDownsize > 0)
		{
			buff.Allocate(BLOCKSIZE);
			int n = this->FTPRead(buff.BufferHead(), 1, BLOCKSIZE);
			if (n <= 0)
				break;
			loadsize += n;
			buff.Allocate(n);
			needDownsize -= n;
			tmpwritecount += fwrite(buff.BufferHead(), 1, buff.BufferSize(), wfile);
		}
		fflush(wfile);
		防止句柄耗尽,及时关闭
		fclose(wfile);
		return tmpwritecount;
	}
}
size_t cpl::GsVSIVirtualHandle::Read(void * pBufferIn, size_t nSize, size_t nCount)
{
	unsigned long long needsize = nCount * nSize;
	if (filesize >0  && needsize + curOffset > filesize)
		return 0;

	int n = ReadLocal(pBufferIn, nSize, nCount);
	if (n > 0)
		return n;

	size_t writecount = Read2Local(pBufferIn, nSize, nCount);

	return  ReadLocal(pBufferIn, nSize, nCount);
}
cpl::GsVSIVirtualHandle::GsVSIVirtualHandle(GeoStar::Utility::GsVirtualFile * file)
{
	m_File = file;
	GsString tmpFile = GsFileSystem::TemporaryFolder();
	m_tmpFilePath = GsFileSystem::Combine(tmpFile.c_str(), GsMath::NewGUID().c_str());

	filesize = m_File->Size();
}

cpl::GsVSIVirtualHandle::~GsVSIVirtualHandle()
{
	if (m_File)
	{
		GsFile::Delete(m_tmpFilePath.c_str());
		m_File = NULL;
	}

}


size_t cpl::GsVSIVirtualHandle::FTPRead(void * pBufferIn, size_t nSize, size_t nCount)
{
	if (!m_ReadHandle)
		m_ReadHandle = m_File->OpenRead();
	if (!m_ReadHandle)
		return 0;
	long long nRawCount = nSize * nCount;
	if (0 == nRawCount)
		return 0;
	int r = m_ReadHandle->RawRead((unsigned char*)pBufferIn, nRawCount);
	//int retrycount = 3;
	//for (int i = 0; i < retrycount; i++)
	//{
	//	if (r != 0)
	//		return r;

	//	m_ReadHandle = m_File->OpenRead();
	//	if (!m_ReadHandle)
	//		return 0;
	//	long long nRawCount = nSize * nCount;
	//	if (0 == nRawCount)
	//		return 0;
	//	r = m_ReadHandle->RawRead((unsigned char*)pBufferIn, nRawCount);

	//}
	return r;
}

size_t cpl::GsVSIVirtualHandle::Write(const void * pBuffer, size_t nSize, size_t nCount)
{
	return 0;
}
//return 0 on success or -1 on failure.
int cpl::GsVSIVirtualHandle::Close()
{
	防止句柄耗尽,及时关闭
	//fclose(rfile);
	//fclose(wfile);
	return 0;
}

int cpl::GsVSIVirtualHandle::ReadMultiRange(int nRanges, void ** ppData,
	const vsi_l_offset* panOffsets,
	const size_t* panSizes)
{
	int nRet = 0;
	const vsi_l_offset nCurOffset = Tell();
	for (int i = 0; i < nRanges; i++)
	{
		if (Seek(panOffsets[i], SEEK_SET) < 0)
		{
			nRet = -1;
			break;
		}

		const size_t nRead = Read(ppData[i], 1, panSizes[i]);
		if (panSizes[i] != nRead)
		{
			nRet = -1;
			break;
		}
	}

	Seek(nCurOffset, SEEK_SET);

	return nRet;
}


cpl::GsVSIFilesystemHandler::GsVSIFilesystemHandler()
{
}

cpl::GsVSIFilesystemHandler::GsVSIFilesystemHandler(GsVirtualFolder * foler)
{
	m_Root = foler;
}
cpl::GsVSIFilesystemHandler::GsVSIFilesystemHandler(const KERNEL_NAME::GsConnectProperty & conn, const UTILITY_NAME::GsConfig & conf)
{
	Init(conn, conf);
}
cpl::GsVSIFilesystemHandler::GsVSIFilesystemHandler(const char * file, KERNEL_NAME::GsGeoDatabase * pGdb)
{
	Init(file, pGdb);
}
GsString OpenType(const GsConnectProperty& conn, const GsConfig &conf)
{
	auto strVec = GsStringHelp::Split(conn.Server, ":");
	if (strVec.size() <= 0)
		return "Local";

	if (0 == GsStringHelp::CompareNoCase("Local", strVec[0]))
	{
		return "Local";
	}
	else if (0 == GsStringHelp::CompareNoCase("FTP", strVec[0]))
	{
		return "FTP";
	}
	else if (0 == GsStringHelp::CompareNoCase("WebDAV", strVec[0]))
	{
		return "WebDAV";
	}
	else if (0 == GsStringHelp::CompareNoCase("OwnCloud", strVec[0]))
	{
		return"OwnCloud";
	}
	else if (0 == GsStringHelp::CompareNoCase("NFS", strVec[0]))
	{
		return"NFS";
	}
	else
	{
		return "Local";
	}
}
KERNEL_NAME::GsFileSystemClassPtr GetFS(const GsConnectProperty& conn, GsConfig &conf)
{
	UTILITY_NAME::GsObjectInstancerPtr ptrInstancer = UTILITY_NAME::GsClassFactory::CreateInstanceT<UTILITY_NAME::GsObjectInstancer>("FileSystemDataSourceFactory");
	if (!ptrInstancer)
	{
		GS_W << "can't instance FileDataRoomCatalog ,need load gsspecialdatasource library first";
		return 0;
	}
	if (ptrInstancer->Exists("Server"))
		ptrInstancer->PropertyRef<UTILITY_NAME::GsString>("Server") = conn.Server;
	if (ptrInstancer->Exists("Database"))
		ptrInstancer->PropertyRef<UTILITY_NAME::GsString>("Database") = conn.Database;
	if (ptrInstancer->Exists("Password"))
		ptrInstancer->PropertyRef<UTILITY_NAME::GsString>("Password") = conn.Password;
	if (ptrInstancer->Exists("Port"))
		ptrInstancer->PropertyRef<int>("Port") = conn.Port;
	if (ptrInstancer->Exists("User"))
		ptrInstancer->PropertyRef<UTILITY_NAME::GsString>("User") = conn.User;
	if (ptrInstancer->Exists("Version"))
		ptrInstancer->PropertyRef<int>("Version") = conn.Version;
	if (ptrInstancer->Exists("Config"))
		ptrInstancer->PropertyRef<UTILITY_NAME::GsConfig>("Config") = conf;

	conf.Child("OpenType").Value(OpenType(conn, conf).c_str());

	return ptrInstancer->CreateInstanceT<KERNEL_NAME::GsFileSystemClass>();
}

bool cpl::GsVSIFilesystemHandler::Init(const char*file, GsGeoDatabase* pGdb)
{
	GsConnectProperty conn = pGdb->ConnectProperty();
	m_Conf = pGdb->Config();
	m_Conn = conn;
	m_Conn.Server = file;
	m_ptrFsc = GetFS(m_Conn, m_Conf);
	if (m_ptrFsc)
		m_Root = m_ptrFsc->RootFolder();
	return !m_Root || !m_ptrFsc;
}
bool cpl::GsVSIFilesystemHandler::Init(const GsConnectProperty & conn, const GsConfig & conf)
{
	m_Conf = conf;
	m_Conn = conn;
	m_ptrFsc = GetFS(m_Conn, m_Conf);
	if (m_ptrFsc)
		m_Root = m_ptrFsc->RootFolder();
	return !m_Root || !m_ptrFsc;
}
GsString NonStandardUrlIP(const char * pszFilename) {
	GsString tmpstr = pszFilename;
	tmpstr = GsStringHelp::Replace(tmpstr.c_str(), "\\\\", "//");
	tmpstr = GsStringHelp::Replace(tmpstr.c_str(), "\\", "/");
	if (tmpstr.find("//") != tmpstr.npos)
	{
		tmpstr = GsStringHelp::Replace(tmpstr.c_str(), "//", "/");
		auto tmps = GsStringHelp::Split(tmpstr.c_str(), "/");
		if (tmps.size() >= 2)
		{
			tmpstr = tmps[1];
		}
	}
	return tmpstr;
}

GsString NonStandardUrlBody(const char * pszFilename)
{
	GsString tmpstr = pszFilename;
	tmpstr = GsStringHelp::Replace(tmpstr.c_str(), "\\\\", "//");
	tmpstr = GsStringHelp::Replace(tmpstr.c_str(), "\\", "/");
	try
	{
		if (tmpstr.find("//") == tmpstr.npos)
			return tmpstr;
		if (tmpstr.size() > 6) {
			GsString mpstr = tmpstr.substr(6, tmpstr.size());
			auto st = mpstr.find("/");
			if (st != mpstr.npos)
			{
				tmpstr = mpstr.substr(st + 1, mpstr.size());
			}
		}
	}
	catch (const std::exception & ex)
	{
		GS_W << ex.what();
	}



	return tmpstr;
}

VSIVirtualHandle * cpl::GsVSIFilesystemHandler::Open(const char * pszFilename, const char * pszAccess, bool bSetError, CSLConstList papszOptions)
{
	if (!m_Root)
		return 0;

	GsString strtmp = NonStandardUrlBody(pszFilename);
	auto file = m_Root->SubFile(strtmp.c_str());
	if (!file)
		return 0;
	return new GsVSIVirtualHandle(file);
}

int cpl::GsVSIFilesystemHandler::Stat(const char * pszFilename, VSIStatBufL * pStatBuf, int nFlags)
{
	if (!m_Root)
		return -1;

	GsVirtualFileSystemPtr ptrfg = m_Root;
	GsString strtmp = NonStandardUrlBody(pszFilename);
	GsVirtualFilePtr file = 0;
	file = m_Root->SubFile(strtmp.c_str());
	if (!file)
		return -1;

	GsAny nChunkSize = file->Attributes()->Attribute(GsVirtualFilesSystemAttributeType::eChunkSize);
	GsAny nFileID = file->Attributes()->Attribute(GsVirtualFilesSystemAttributeType::eFileID);
	GsAny nFileSize = file->Size(); //file->Attributes()->Attribute(GsVirtualFilesSystemAttributeType::eFileSize);

	pStatBuf->st_size = nFileSize.Empty() ? 0 : nFileSize.AsLongLong();
	//pStatBuf->st_dev = nFileSize.Empty() ? 0 : nFileSize.AsLongLong();
	return 0;
}

//void VSIInstallCurlFileHandler(void)
//{
//	VSIFilesystemHandler* poHandler = new cpl::VSICurlFilesystemHandler;
//	VSIFileManager::InstallHandler("/vsicurl/", poHandler);
//	VSIFileManager::InstallHandler("/vsicurl?", poHandler);
//}
//
//void VSIInstallCurlStreamingFileHandler(void)
//{
//	VSIFileManager::InstallHandler("/vsicurl_streaming/",
//		new VSICurlStreamingFSHandler);
//}

最后得效果就是可以跟访问本地文件一样访问远程得tif和img文件, 其他格式没有尝试.

但是这里有个限制我这里用得是FTP协议,  无法实现seek接口,  只能将文件一段段下载下来, 然后可以操作,  如果有那种远程资源访问能支持向前和向后seek文件就能完美了,  我也不用支持缓存了.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值