bug修改之后的代码:
import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.Map; import java.util.Timer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.lubansoft.test.upload.po.UploadRecord; import com.lubansoft.test.upload.po.UploadResult; public class FileUpload { /** * 接收上传的参数和文件 */ public UploadResult upload(HttpServletRequest request,HttpServletResponse response){ UploadResult uploadResult = new UploadResult(); try { InputStream in = new BufferedInputStream(request.getInputStream(),10240); String requestHeader = readLine(in); String line = null; Map<String,String> param = new HashMap<String, String>(); Map<String,String> fileUploadResults = new HashMap<String, String>(); uploadResult.setParams(param); uploadResult.setFileUploadResults(fileUploadResults); /** * 判断是否是文件/参数 */ if("------parameters------".equals(requestHeader)){ /** * 上传参数的格式: * 第一行: ------parameters------ * 第二行:"parameterLength=" + parameterLength * 第三行: key1=value1&key2=value2........ */ line = readLine(in); int length = 0; if(line != null){ if("parameterLength".equals(line.split("=")[0])){ length = Integer.parseInt(line.split("=")[1]); } } byte[] b = null; if(length != 0){ b = new byte[length]; in.read(b, 0, length); line = new String(b,0,length,"utf-8"); } if(line != null){ String[] params = line.split("&"); if(params!=null && params.length>0){ for(String keyValue : params){ param.put(keyValue.split("=")[0], keyValue.split("=")[1]); } } } System.out.println(param); /** *读取最后的一个换行符:\n */ readLine(in); if("------file begin------".equals(line=readLine(in))){ startTimerAndUploadFile(request, in, fileUploadResults); } }else if("------file begin------".equals(requestHeader)){ startTimerAndUploadFile(request, in, fileUploadResults); } } catch (Exception e) { e.printStackTrace(); } finally { try { response.getWriter().write("upload complete !"); } catch (IOException e) { e.printStackTrace(); } } return uploadResult; } /** * 启动定时器,开始上传文件 */ public void startTimerAndUploadFile(HttpServletRequest request,InputStream in,Map<String,String> fileUploadResults){ Timer timer = new Timer(); UploadRateTimerTask task = new UploadRateTimerTask(request); long interval = FileUploadUtil.getTimerInfo(request) != null ? Long.parseLong(FileUploadUtil.getTimerInfo(request)[0]) : 1000; timer.schedule(task, 0 , interval); saveFile(in, request, task,fileUploadResults); timer.cancel(); } //保存文件 public void saveFile(InputStream in ,HttpServletRequest request,UploadRateTimerTask task,Map<String,String> fileUploadResults){ String line=null; RandomAccessFile raf = null; UploadRecord uploadRecord = null; String fileMD5_client = null; String fileMD5_server = null; int blockSize = FileUploadUtil.getBlockSize(); try { task.setTotal_uploadSize(0); //先读到该文件的信息头“fileName=ddd&fileSize=xxx&uploadSize=aaa” line = readLine(in); if(line != null){ uploadRecord = FileUploadUtil.getUploadRecoreFromString(line); fileMD5_client = uploadRecord.getFileMD5(); HttpSession session = request.getSession(); String folder = session.getServletContext().getRealPath(UploadConfigConstants.UPLOAD_FILE); String filepath = folder+File.separator + FileUploadUtil.getFileNameByUploadRecord(uploadRecord); File file = new File(filepath); if(! file.getParentFile().exists()){ file.getParentFile().mkdirs(); } if(! file.exists()){ file.createNewFile(); } /** * 先将properties文件放到临时文件夹下(tmep) */ String config_path = session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT) + File.separator + "temp/" + FileUploadUtil.getConfigFileNameByUploadRecord(uploadRecord); File config_file = new File(config_path); if(! config_file.getParentFile().exists()){ config_file.getParentFile().mkdirs(); } if (! config_file.exists()) { config_file.createNewFile(); } raf = new RandomAccessFile(file, "rwd"); raf.setLength(uploadRecord.getFileSize()); //将读取文件的指针移动到断点位置 raf.seek(uploadRecord.getUploadSize()); //计算出从断点位置到文件结尾,还剩下的文件大小 long leftSize = uploadRecord.getFileSize()-uploadRecord.getUploadSize(); //本次上传,上传完成的文件大小 long completeSize = 0l; /** * 接下来是文件的内容 */ int n=-1; byte[] b = new byte[2048]; int addSize = 0; /** * 在上传文件的时候,再去判断一下是否已经有程序在上传该文件 * 作为确认,防止万一 */ boolean isStarted = false; if(FileUploadUtil.isUploadStarted(request, uploadRecord)){ isStarted = true; } /** * 为定时器动态绑定uploadRecord对象 */ task.setUploadRecord(uploadRecord); /** * 判断文件从断点开始,还有多大文件需要传输,如果太小(小于 1 k )就要特殊处理 * 因为这时候不能使用 in.read(b) ,而只能使用 in.read(b,0,leftSize) */ if((completeSize + b.length) <= leftSize){ while((n=in.read(b)) != -1){ if(! isStarted){ raf.write(b,0,n); completeSize += n; addSize += n; task.setTotal_uploadSize(completeSize); if(addSize >= blockSize){ FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; } } /** * 最后一部分特殊处理 */ if((completeSize + b.length) >= leftSize){ int num = -1; while(true){ /** * num表示最后一部分还剩下多少,也就是应该读取的大小 * n表示真正读取到的部分是多少,也就是实际上读取到的大小 * n和num可能并不相同,一直到leftSize == completeSize才说明最后一部分处理完成 */ num = (int)(leftSize-completeSize); n = in.read(b, 0, num); completeSize += n; if(! isStarted){ raf.write(b,0,n); addSize += n; task.setTotal_uploadSize(completeSize); if(addSize >= blockSize){ FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; } } /** * 如果leftSize == completeSize说明最后一部分处理完成 */ if(leftSize == completeSize){ raf.close(); FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; //完成一个文件的上传,将该文件的完成百分比改为100% task.setTaskComplete(); //完成一个文件的上传,将该文件的isStopped改为stopped FileUploadUtil.setConfigFileStoped(request, uploadRecord); fileMD5_server = MD5Util.getFileMD5String(file); if(fileMD5_server != null && fileMD5_client != null){ if(fileMD5_server.equals(fileMD5_client)){ fileUploadResults.put(file.getName(), "OK"); /** * 文件上传完成并且校验成功,则将其配置文件转移到已上传成功的文件夹 */ FileUploadUtil.MoveFile(config_file, session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT)); } }else{ /** * 文件上传完成,但是文件已损坏 */ fileUploadResults.put(file.getName(), "DAMAGED"); } readLine(in); line = readLine(in); if("------file begin------".equals(line)){ saveFile(in, request,task,fileUploadResults); } break; } } } } }else{//判断文件从断点开始,如果剩余文件太小(小于 1 k )就要特殊处理 int num = -1; while(true){ num = (int)(leftSize-completeSize); n = in.read(b, 0, num); completeSize += n; if(! isStarted){ raf.write(b,0,n); addSize += n; task.setTotal_uploadSize(completeSize); if(addSize >= blockSize){ FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; } } if(leftSize == completeSize){ raf.close(); FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; //完成一个文件的上传,将该文件的完成百分比改为100% task.setTaskComplete(); //完成一个文件的上传,将该文件的isStopped改为stopped FileUploadUtil.setConfigFileStoped(request, uploadRecord); fileMD5_server = MD5Util.getFileMD5String(file); if(fileMD5_server != null && fileMD5_client != null){ if(fileMD5_server.equals(fileMD5_client)){ fileUploadResults.put(file.getName(), "OK"); /** * 文件上传完成并且校验成功,则将其配置文件转移到已上传成功的文件夹 */ FileUploadUtil.MoveFile(config_file, session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT)); } }else{ /** * 文件上传完成,但是文件已损坏 */ fileUploadResults.put(file.getName(), "DAMAGED"); } readLine(in); line = readLine(in); if("------file begin------".equals(line)){ saveFile(in, request,task,fileUploadResults); } break; } } } /** * 文件上传完成,将状态修改为 “stoped” */ if(uploadRecord != null){ FileUploadUtil.setConfigFileStoped(request, uploadRecord); } } // } } catch (IOException e) { /** * 上传过程中出现异常,将状态修改为 “stoped” */ e.printStackTrace(); }finally{ /** * 上传过程中出现异常,将状态修改为 “stoped” */ if(uploadRecord != null){ FileUploadUtil.setConfigFileStoped(request, uploadRecord); } if(raf != null){ try { raf.close(); } catch (IOException e1) { e1.printStackTrace(); } raf = null; } } } /** * 读取一行内容 */ private String readLine(InputStream in) { return FileUploadUtil.readLine(in); } }
之前的代码:
import java.io.BufferedInputStream; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.RandomAccessFile; import java.util.HashMap; import java.util.Map; import java.util.Timer; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpSession; import com.lubansoft.test.upload.po.UploadRecord; import com.lubansoft.test.upload.po.UploadResult; public class FileUpload { /** * 接收上传的参数和文件 */ public UploadResult upload(HttpServletRequest request,HttpServletResponse response){ UploadResult uploadResult = new UploadResult(); try { InputStream in = new BufferedInputStream(request.getInputStream(),10240); String requestHeader = readLine(in); String line = null; Map<String,String> param = new HashMap<String, String>(); Map<String,String> fileUploadResults = new HashMap<String, String>(); uploadResult.setParams(param); uploadResult.setFileUploadResults(fileUploadResults); /** * 判断是否是文件/参数 */ if("------parameters------".equals(requestHeader)){ /** * 上传参数的格式: * 第一行: ------parameters------ * 第二行:"parameterLength=" + parameterLength * 第三行: key1=value1&key2=value2........ */ line = readLine(in); int length = 0; if(line != null){ if("parameterLength".equals(line.split("=")[0])){ length = Integer.parseInt(line.split("=")[1]); } } byte[] b = null; if(length != 0){ b = new byte[length]; in.read(b, 0, length); line = new String(b,0,length,"utf-8"); } if(line != null){ String[] params = line.split("&"); if(params!=null && params.length>0){ for(String keyValue : params){ param.put(keyValue.split("=")[0], keyValue.split("=")[1]); } } } System.out.println(param); /** *读取最后的一个换行符:\n */ readLine(in); if("------file begin------".equals(line=readLine(in))){ startTimerAndUploadFile(request, in, fileUploadResults); } }else if("------file begin------".equals(requestHeader)){ startTimerAndUploadFile(request, in, fileUploadResults); } } catch (Exception e) { e.printStackTrace(); } finally { try { response.getWriter().write("upload complete !"); } catch (IOException e) { e.printStackTrace(); } } return uploadResult; } /** * 启动定时器,开始上传文件 */ public void startTimerAndUploadFile(HttpServletRequest request,InputStream in,Map<String,String> fileUploadResults){ Timer timer = new Timer(); UploadRateTimerTask task = new UploadRateTimerTask(request); long interval = FileUploadUtil.getTimerInfo(request) != null ? Long.parseLong(FileUploadUtil.getTimerInfo(request)[0]) : 1000; timer.schedule(task, 0 , interval); saveFile(in, request, task,fileUploadResults); timer.cancel(); } //保存文件 public void saveFile(InputStream in ,HttpServletRequest request,UploadRateTimerTask task,Map<String,String> fileUploadResults){ String line=null; // String fileName=null; // String fileSize = null; // String uploadSize=null; RandomAccessFile raf = null; UploadRecord uploadRecord = null; String fileMD5_client = null; String fileMD5_server = null; int blockSize = FileUploadUtil.getBlockSize(); try { task.setTotal_uploadSize(0); //先读到该文件的信息头“fileName=ddd&fileSize=xxx&uploadSize=aaa” line = readLine(in); if(line != null){ /*String[] info = line.split("&"); if(info != null && info.length>0){ for(String s: info){ if("fileName".equals(s.split("=")[0])){ fileName = s.split("=")[1]; }else if("fileSize".equals(s.split("=")[0])){ fileSize = s.split("=")[1]; }else if("uploadSize".equals(s.split("=")[0])){ uploadSize = s.split("=")[1]; }else if("fileMD5".equals(s.split("=")[0])){ fileMD5_client = s.split("=")[1]; } }*/ uploadRecord = FileUploadUtil.getUploadRecoreFromString(line); fileMD5_client = uploadRecord.getFileMD5(); HttpSession session = request.getSession(); String folder = session.getServletContext().getRealPath(UploadConfigConstants.UPLOAD_FILE); String filepath = folder+File.separator + FileUploadUtil.getFileNameByUploadRecord(uploadRecord); File file = new File(filepath); if(! file.getParentFile().exists()){ file.getParentFile().mkdirs(); } if(! file.exists()){ file.createNewFile(); } /** * 先将properties文件放到临时文件夹下(tmep) */ String config_path = session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT) + File.separator + "temp/" + FileUploadUtil.getConfigFileNameByUploadRecord(uploadRecord); File config_file = new File(config_path); if(! config_file.getParentFile().exists()){ config_file.getParentFile().mkdirs(); } if (! config_file.exists()) { config_file.createNewFile(); } raf = new RandomAccessFile(file, "rwd"); // if(file.length() != Long.parseLong(fileSize)){ // raf.setLength(Long.parseLong(fileSize)); // } raf.setLength(uploadRecord.getFileSize()); //将读取文件的指针移动到断点位置 raf.seek(uploadRecord.getUploadSize()); //计算出从断点位置到文件结尾,还剩下的文件大小 long leftSize = uploadRecord.getFileSize()-uploadRecord.getUploadSize(); //本次上传,上传完成的文件大小 long completeSize = 0l; /** * 接下来是文件的内容 */ int n=-1; byte[] b = new byte[2048]; int total = 0; int addSize = 0; /*uploadRecord = new UploadRecord(); uploadRecord.setFileName(uploadRecord.getFileName()); uploadRecord.setFileSize(uploadRecord.getFileSize()); uploadRecord.setIp(request.getRemoteAddr()); uploadRecord.setUploadSize(Long.parseLong(uploadSize));*/ /** * 在上传文件的时候,再去判断一下是否已经有程序在上传该文件 * 作为确认,防止万一 */ boolean isStarted = false; if(FileUploadUtil.isUploadStarted(request, uploadRecord)){ isStarted = true; } /** * 为定时器动态绑定uploadRecord对象 */ task.setUploadRecord(uploadRecord); /** * 判断文件从断点开始,还有多大文件需要传输,如果太小(小于 1 k )就要特殊处理 * 因为这时候不能使用 in.read(b) ,而只能使用 in.read(b,0,leftSize) */ if((completeSize + b.length) <= leftSize){ while((n=in.read(b)) != -1){ if(! isStarted){ raf.write(b,0,n); total += n; completeSize += n; addSize += n; task.setTotal_uploadSize(completeSize); if(addSize >= blockSize){ FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; } } //判断是否已经到达最后一部分 if((completeSize + b.length) >= leftSize){ n = (int)(leftSize-completeSize); 问题就出在这里,因为客户端使用的是conn.setChunkedStreamingMode(10*1024);也就是块的概念,每次传输给服务器10*1024大小的内容,那么就可能会出现一个文件的最后一部分,被分在了两块里面,也就是分两次上传,这种情况下,in.read(b,0,n)就不能读取到n个字节的内容,而是少于n个,因此,这里需要获取到真正读取到的字节数:realNum = in.read(b, 0, n);然后判断(n-realNum)的大小,如果大于0,说明还没有读取完成,就需要循环,所以正确的方式应该如下: /** * 最后一部分特殊处理 */ if((completeSize + b.length) >= leftSize){ int num = -1; while(true){ /** * num表示最后一部分还剩下多少 * n表示真正读取到的部分是多少 * n和num可能并不相同 */ num = (int)(leftSize-completeSize); n = in.read(b, 0, num); completeSize += n; if(! isStarted){ raf.write(b,0,n); addSize += n; task.setTotal_uploadSize(completeSize); if(addSize >= blockSize){ FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; } } /** * 如果leftSize == completeSize说明最后一部分处理完成 */ if(leftSize == completeSize){ raf.close(); FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; //完成一个文件的上传,将该文件的完成百分比改为100% task.setTaskComplete(); //完成一个文件的上传,将该文件的isStopped改为stopped FileUploadUtil.setConfigFileStoped(request, uploadRecord); fileMD5_server = MD5Util.getFileMD5String(file); if(fileMD5_server != null && fileMD5_client != null){ if(fileMD5_server.equals(fileMD5_client)){ fileUploadResults.put(file.getName(), "OK"); /** * 文件上传完成并且校验成功,则将其配置文件转移到已上传成功的文件夹 */ FileUploadUtil.MoveFile(config_file, session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT)); } }else{ /** * 文件上传完成,但是文件已损坏 */ fileUploadResults.put(file.getName(), "DAMAGED"); } readLine(in); line = readLine(in); if("------file begin------".equals(line)){ saveFile(in, request,task,fileUploadResults); } break; } } } 而不是: in.read(b, 0, n); if(! isStarted){ raf.write(b,0,n); total += n; completeSize += n; addSize += n; raf.close(); task.setTotal_uploadSize(completeSize); FileUploadUtil.updateUploadSize(request, uploadRecord, addSize); addSize = 0; //完成一个文件的上传,将该文件的完成百分比改为100% task.setTaskComplete(); //完成一个文件的上传,将该文件的isStopped改为stopped FileUploadUtil.setConfigFileStoped(request, uploadRecord); fileMD5_server = MD5Util.getFileMD5String(file); if(fileMD5_server != null && fileMD5_client != null){ if(fileMD5_server.equals(fileMD5_client)){ fileUploadResults.put(file.getName(), "OK"); /** * 文件上传完成并且校验成功,则将其配置文件转移到已上传成功的文件夹 */ FileUploadUtil.MoveFile(config_file, session.getServletContext().getRealPath(UploadConfigConstants.BREAK_POINT)); } }else{ /** * 文件上传完成,但是文件已损坏 */ fileUploadResults.put(file.getName(), "DAMAGED"); } file = null; } readLine(in); line = readLine(in); if("------file begin------".equals(line)){ saveFile(in, request,task,fileUploadResults); } break; } } System.out.println(completeSize); }else{//判断文件从断点开始,如果剩余文件太小(小于 1 k )就要特殊处理 in.read(b, 0, (int)leftSize); if(!isStarted){ raf.write(b, 0, (int)leftSize); } raf.close(); task.setTotal_uploadSize(leftSize); FileUploadUtil.updateUploadSize(request, uploadRecord, leftSize); //完成一个文件的上传,将该文件的完成百分比改为100% task.setTaskComplete(); //完成一个文件的上传,将该文件的isStopped改为stopped FileUploadUtil.setConfigFileStoped(request, uploadRecord); readLine(in); line = readLine(in); if("--file begin--".equals(line)){ saveFile(in, request,task,fileUploadResults); fileMD5_server = MD5Util.getFileMD5String(file); if(fileMD5_server != null && fileMD5_client != null){ if(fileMD5_server.equals(fileMD5_client)){ fileUploadResults.put(file.getName(), "OK"); } }else{ fileUploadResults.put(file.getName(), "DAMAGED"); } }else if("------complete------".equals(line)){ in.close(); in = null; System.out.println("------complete------"); } } /** * 文件上传完成,将状态修改为 “stoped” */ if(uploadRecord != null){ FileUploadUtil.setConfigFileStoped(request, uploadRecord); } } // } } catch (IOException e) { /** * 上传过程中出现异常,将状态修改为 “stoped” */ e.printStackTrace(); }finally{ /** * 上传过程中出现异常,将状态修改为 “stoped” */ if(uploadRecord != null){ FileUploadUtil.setConfigFileStoped(request, uploadRecord); } if(raf != null){ try { raf.close(); } catch (IOException e1) { e1.printStackTrace(); } raf = null; } } } /** * 读取一行内容 */ private String readLine(InputStream in) { return FileUploadUtil.readLine(in); } }
这个问题的解决让我进一步明白,出现问题的时候,尽量从自己身上找问题,这才是正道
相关推荐
基于springboot大学生就业信息管理系统源码数据库文档.zip
基于java的驾校收支管理可视化平台的开题报告
时间序列 原木 间隔5秒钟 20241120
毕业设计&课设_基于 Vue 的电影在线预订与管理系统:后台 Java(SSM)代码,为毕业设计项目.zip
基于springboot课件通中小学教学课件共享平台源码数据库文档.zip
基于java的网上购物商城的开题报告
Delphi人脸检测与识别Demo1fdef-main.zip
基于java的咖啡在线销售系统的开题报告
基于java的自助医疗服务系统的开题报告.docx
内容概要:本文档全面介绍了Visual Basic(VB)编程语言的基础知识和高级应用。首先概述了VB的基本特性和开发环境,随后详细讲述了VB的数据类型、变量、运算符、控制结构、数组、过程与函数、变量作用域等内容。接着介绍了窗体设计、控件使用、菜单与工具栏的设计,文件操作、数据库访问等关键知识点。最后讨论了VB的学习方法、发展历史及其在桌面应用、Web应用、数据库应用、游戏开发和自动化脚本编写等领域的广泛应用前景。 适合人群:初学者和中级程序员,尤其是希望快速掌握Windows桌面应用开发的人群。 使用场景及目标:①掌握VB的基础语法和开发环境;②学会使用VB创建复杂的用户界面和功能完整的应用程序;③理解数据库操作、文件管理和网络编程等高级主题。 其他说明:Visual Basic是一种简单易学且功能强大的编程语言,尤其适合用于开发Windows桌面应用。文中不仅覆盖了基础知识,还包括了大量的实用案例和技术细节,帮助读者快速提升编程技能。
基于java的疫情期间高校防控系统开题报告.docx
基于springboot+vue社区老年人帮扶系统源码数据库文档.zip
基于java的超市商品管理系统的开题报告.docx
基于SpringBoot房屋买卖平台源码数据库文档.zip
xdu限通院23微处理器系统与应用大作业(两只老虎),适应于汇编语言keil软件,
<项目介绍> - 新闻类网站系统,基于SSM(Spring、Spring MVC、MyBatis)+MySQL开发,高分成品毕业设计,附带往届论文 - 不懂运行,下载完可以私聊问,可远程教学 1、该资源内项目代码都经过测试运行成功,功能ok的情况下才上传的,请放心下载使用! 2、本项目适合计算机相关专业(如计科、人工智能、通信工程、自动化、电子信息等)的在校学生、老师或者企业员工下载学习,也适合小白学习进阶,当然也可作为毕设项目、课程设计、作业、项目初期立项演示等。 3、如果基础还行,也可在此代码基础上进行修改,以实现其他功能,也可用于毕设、课设、作业等。 下载后请首先打开README.md文件(如有),仅供学习参考, 切勿用于商业用途。 --------
基于java的学生网上请假系统的开题报告.docx
社会经济繁荣发展的今天,电子商务得到了飞速发展,网上交易越来越彰显出其独特的优越性,在人们的日常生活中,出现了各种类型的交易网站。其中一个就是车辆易主交易网站,它是一个服务于用户买卖二手车辆的交易网站,为用户提供了平等互利、方便快捷的网上交易平台,通过这一类型的网站,用户可自由出售和购买车辆。 本课题主要根据车辆本身的特性,充分发挥互联网的特点与优势,构建一个以二手车辆为商品、基于互联网平台的车辆易主业务交易管理系统,并根据车辆易主业务交易管理系统的应用需求,进行需求分析,进而对网站系统作规划设计。采用IDEA为运行平台,以SSH为框架,运用HTML语言、JSP技术、MySql数据库、JSP与后台数据库链接等关键技术建设二手车网上交易系统,构建车辆易主交易系统的会员注册与登录,网站首页展示、用户发布商品车辆,用户求购商品车辆,分页浏览、购物系统、用户后台管理、管理员用户后台管理等功能,并使这些功能得以实现并更好为用户服务。网站整体构建完成且测试成功后,用户可以进入网站进行注册、登录,登录后,用户可以在网站上发布自己的闲置车辆或者寻找想要购买的车辆,还可以收藏车辆,管理发布和收藏的车辆,
SQLite3的向量扩展库,windows dll,版本0.1.5
基于C++实现(控制台)商品库存管理系统