背景:
儿子最近喜欢上了用儿童手表听故事,但是手表边里的应用免费内容很少,会员一年要300多,这么一笔巨款,怎能承担的起,所以打算自己开发一个专属于儿子的听书app。
最终效果:
架构:
后端由两个服务组成,一个文件服务用于预览图片和在线听故事。一个接口服务用于 获取故事列表和某个故事的详细内容。
前端app用flutter开发,一共三个页面。故事列表页,详细故事页,和播放页面。
服务端代码:
#include "crow.h"
#include <crow/json.h>
#include <iostream>
#include <dirent.h>
#include <string>
#include <vector>
#include <initializer_list>using namespace std;// 获取文件后缀名
std::string getFileExtension(const std::string& filename) {size_t lastDot = filename.find_last_of(".");if (lastDot != std::string::npos) {return filename.substr(lastDot + 1);}return ""; // 如果没有后缀,返回空字符串或其他你认为合适的默认值
}//遍历文件夹
std::vector<string> scanDir(const char* dir, initializer_list<string> subStrinList){DIR* directory;struct dirent* entry;//获取所有后缀string suffixList("");for(auto beg=subStrinList.begin(); beg!=subStrinList.end(); ++beg)suffixList += *beg;std::cout<<"后缀 "<<suffixList<<std::endl;std::vector<std::string> allFile;directory = opendir(dir);if(directory!=nullptr){while((entry=readdir(directory))!=nullptr){std::string filename = entry->d_name;std::string fileSuffix = getFileExtension(filename);if(filename!="."&&filename!=".."&&fileSuffix.length()>0&&suffixList.find(fileSuffix)!=std::string::npos){allFile.push_back(filename);}}closedir(directory);}else{std::cerr << "Failed to open directory." << std::endl;}return allFile;
}std::string url_decode(const std::string &str) {std::string result;std::istringstream iss(str);char ch;int i;while (iss >> std::noskipws >> ch) {if (ch == '%') {if (!(iss >> std::hex >> i)) {break;}result += static_cast<char>(i);} else if (ch == '+') {result += ' ';} else {result += ch;}}return result;
}int main(int argc, char **argv)
{crow::SimpleApp app;CROW_ROUTE(app, "/")([](){crow::response res;res.set_header("Content-Type", "text/plain; charset=utf-8");res.body = "你好啊,小朋友!";return res;});// 获取所有条目CROW_ROUTE(app, "/getAllEntry")([](){crow::response res;res.set_header("Content-Type", "text/plain; charset=utf-8");crow::json::wvalue j;std::vector<std::string> allEntry = scanDir("./data",{"jpg"});for(int i=0;i<allEntry.size();i++){j[i]=allEntry.at(i);}std::cout<<"allEntry "<<crow::json::dump(j)<<std::endl;res.body = crow::json::dump(j);return res; });//获取某个条目的所有内容CROW_ROUTE(app, "/getDetailEntry/<string>")([](string name){std::string decodedQuery = url_decode(name);std::cout<<"getDetailEntry "<<decodedQuery<<std::endl;crow::response res;res.set_header("Content-Type", "text/plain; charset=utf-8");std::string dir=std::string("./data/")+decodedQuery;crow::json::wvalue j;std::vector<std::string> allFile = scanDir(dir.data(),{"mp3","m4a"});for(int i=0;i<allFile.size();i++){j[i]=allFile.at(i);}std::cout<<"allFile "<<crow::json::dump(j)<<std::endl;res.body = crow::json::dump(j);return res; });app.port(18080).multithreaded().run();
}
内容:
当有了新的故事后,只需要准备一张故事的预览图,然后一起放到服务器上即可。