当前位置: 首页 > news >正文

做seo推广做网站有用吗门户网站建设依据

做seo推广做网站有用吗,门户网站建设依据,营销案例网站,广州高端品牌网站建设后台管理便捷前言 本文总结Web应用开发中文件上传、下载的方法#xff0c;即从前端表单输入文件并封装表单数据#xff0c;然后请求后端服务器的处理过程#xff1b;从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明#xff0c;并给出了前端常用的axios库的请…前言 本文总结Web应用开发中文件上传、下载的方法即从前端表单输入文件并封装表单数据然后请求后端服务器的处理过程从基础的JavaScript中XmlHttpRequest对象、Fetch API实现上传、下载进行说明并给出了前端常用的axios库的请求方式然后给出了后端Spring实现文件服务器的方法其中就文件上传中遇到的大小限制、跨域请求、外链请求等关键点进行了说明 上传文件 前端上传请求发起的方式若不依赖其他库常使用XMLHttpRequest、Fetch API上传方式若使用第三方库可用Axios请求上传 后端文件服务器的几种处理方式 文保存到磁盘目录 文件存取请求直接根据url地址得出文件所在磁盘目录位置访问速度快 文件直接保存到数据库 若文件比较大影响数据库处理性能但相比保存到磁盘目录的方法可以确保数据更安全可靠性 文件二次请求上传至第三方服务器 后端将接收的表单数据按照第三方接口封装进行请求上传 XMLHttpRequest文件上传 1.表单输入文件 !-- 设置multiple属性时可选择多个文件可在读取文件时使用onchange监听读取到文件内容后预览、或立即上传也可以单独用一个按钮事件控制上传 -- input typefile multiple idfileInput onchangeonShowTextContent(event)/inputpre idfileContent/pre button onclickuploadFile()上传文件/button预览文本内容并显示方法 script function onShowTextContent(event) { // 预览文本内容并显示方法const file event.target.files[0];if (file file.type.startsWith(text/plain) ) {const reader new FileReader();// 定义文件读取完成后的处理逻辑reader.onload function (e) {const fileContent e.target.result;// 显示文本内容document.getElementById(fileContent).textContent fileContent;};// 读取文件为文本内容reader.readAsText(file);} else {console.error(No file selected);}} /script2.XMLHttpRequest上传文件 XMLHttpRequest对象简记为XHR一个内置的浏览器对象提供简单的方式来发送和接收从 URL 的数据而无需刷新整个页面这使得它成为 AJAXAsynchronous JavaScript and XML编程的核心技术之一 AJAX是一种在 Web 应用中通过异步发送 HTTP 请求向服务器获取内容并使用这些新内容更新页面中相关的部分而无需重新加载整个页面的 Web 开发技术这可以让网页更具有响应性由于这种技术在现代Web开发中太过常见以至于AJAX这个术语很少被使用到 XMLHttpRequest具有广泛的兼容性几乎所有浏览器都支持包括老版本浏览器而Fetch API 但在某些旧浏览器如 IE中不支持 function uploadFile() {let fileInput document.getElementById(fileInput); // 获得input文件输入的引用let files fileInput.files; if (files.length 0) {return; // input节点的files是一个文件列表若 files.length 0 表示未选择文件需要跳过}let formData new FormData(); // 创建表单数据对象封装文件for (let file of files) {// file有一些属性常用于校验如 file.name 文件名称file.size 文件字节大小file.type表示文件类型formData.append(files, file); }let request new XMLHttpRequest(); // 创建一个http请求let url http://127.0.0.1:7999/file/upload;request.open(POST, url, true); // 参数1为请求方式参数2为上传服务器地址参数3取true表示异步request.onload function () {if (request.status 200) {console.log(上传完成, request.responseText); } else {console.log(上传出错, request.statusText);}};request.onerror function () {console.log(Error:, request.statusText);};request.send(formData); // 发送表单文件数据 }3. 后端接收文件和保存 先对上传的文件大小、接收文件的类型做判断然后将原始文件名称、大小、内容类型、存储位置等信息到数据库以提供分类、统计分析 Controller层接收上传的文件 // RequestParam 注解标注表单的参数名与form表单一致若该注解缺省则默认参数名称为multipart// 入参 MultipartFile[] 数组表示接受多个文件若写为 MultipartFile对象则只会接受第1个文件cconsumes 指定匹配请求头 content-type PostMapping(value/upload/list,consumes MediaType.MULTIPART_FORM_DATA_VALUE)public MapString,Object uploadFileList(RequestParam(files) MultipartFile[] files);Service层本地存储文件 // controller层调用的文件保存方法对 MultipartFile[] files 中每个对象处理判断文件上传个数等 public MapString,Object saveMultipartFile(MultipartFile multipartFile) throws IOException {MapString, Object results new HashMap();if (multipartFile.getSize() 1024 * 1024 * 10) {throw new RuntimeException(文件大小超过了最大限制10M);}// multipartFile.getContentType() 获取内容类型final String oriName multipartFile.getOriginalFilename(); // 原始文件名不能保证唯一性需要生成一个唯一文件名此处仅简单示例实际开发中文件名称需要确保唯一性String newName System.currentTimeMillis() oriName.substring(oriName.lastIndexOf(.)); //String fullPath System.getProperty(user.dir) / newName;final InputStream inputStream multipartFile.getInputStream();File file new File(fullPath);// 也可以直接使用 multipartFile.transferTo(file);if (file.createNewFile()) {FileCopyUtils.copy(inputStream, Files.newOutputStream(file.toPath()));}// 返回一些有用的信息若是图片需要返回url等results.put(url,http://127.0.0.1:7999/file/newName); return results;}后端转发请求上传到第三方 public ResultFileUploadResponse uploadImageForward(MultipartFile multipartFile) throws IOException {MultiValueMap request new LinkedMultiValueMap(1);ByteArrayResource byteArrayResource new ByteArrayResource(multipartFile.getBytes()) {Override // 需要覆盖默认的方法不然无法获取名称public String getFilename() {return multipartFile.getOriginalFilename();}};// 此处从multipartFile获取byte[],如果是上传本地文件可以使用io获取byte[]request.add(fileName, byteArrayResource);String uploadUrl this.serverBaseUrl/upload; // 假如这是第三方文件上传地址FileUploadResponse response restTemplate.postForObject(uploadUrl, request, FileUploadResponse.class); return new Result(response);}4.文件上传大小配置 在 Spring Boot 项目中可通过配置文件或编程方式来设置文件上传的大小 配置文件方式application.properties 或 application.yml application.properties # 设置单个文件的最大大小为10MB默认情况下为1MB) spring.servlet.multipart.max-file-size10MB # 设置整个请求的最大大小为10MB默认情况下是10MB spring.servlet.multipart.max-request-size10MBapplication.yml spring:servlet:multipart:max-file-size: 10MBmax-request-size: 10MB编程方式 可进行更细粒度的控制可以通过编程配置 MultipartConfigElement实现 Configuration public class ServletMultipartConfig {// 配置文件中添加示例spring.server.MaxFileSize1MBValue(${spring.server.maxFileSize})private DataSize maxFileSize;// 指定单个文件最大尺寸如果超过这个大小后端将不会接受该请求,前端会手到错误Code Details Error: Request Entity Too LargeBeanpublic MultipartConfigElement multipartConfigElement() {// log.info(MaxFileSizemaxFileSize.toMegabytes());MultipartConfigFactory factory new MultipartConfigFactory();factory.setMaxFileSize(maxFileSize); // 但个文件最大大小factory.setMaxRequestSize(maxFileSize); // 整个请求的最大大小return factory.createMultipartConfig();} }5.后端跨域请求配置 文件上传在跨域的情况下会上传失败因此有必要设置服务器的跨域请求规则 springboot的配置类中添加跨域请求配置以拦截器方式实现为例 Configuration public class WebConfig implements WebMvcConfigurer { Beanpublic HandlerInterceptor accessInterceptor() {return new HandlerInterceptor(){Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 若请求头设置了Access-Control-Allow-Credentials为true那Access-control-Allow-Origin不能使用通配符*而必须要使用确切的值response.setHeader(Access-control-Allow-Origin, request.getHeader(Origin));response.setHeader(Access-Control-Allow-Methods, GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH);// 若前端传送了自定义的请求头而没有在这里面设置某些情况下则会提示跨域请求错误not allowed by Access-Control-Allow-Headers in preflight response.response.setHeader(Access-Control-Allow-Headers, x-requested-with,Authorization);response.setHeader(Access-Control-Allow-Credentials, true); // 前端请求设置credentials: include, 这里就必须设置为trueresponse.setHeader(Access-Control-Max-Age, 3600);// 跨域时会首先发送一个option请求这里我们给option请求直接返回正常状态if (request.getMethod().equals(RequestMethod.OPTIONS.name())) {response.setStatus(HttpStatus.OK.value());return false;}return true;}};}Overridepublic void addInterceptors(InterceptorRegistry registry) { // 添加拦截器registry.addInterceptor(this.accessInterceptor()).excludePathPatterns(/error).excludePathPatterns(/webjars/**, /v2/api-docs/**);} }与上述拦截器实现的效果一样也可以采用过滤器实现跨域请求配置 Bean // 注册过滤器public FilterRegistrationBeanCustomCorsFilter customCorsFilterFilterRegistrationBean(){FilterRegistrationBeanCustomCorsFilter registrationBean new FilterRegistrationBean(this.customCorsFilter());registrationBean.setOrder(-1); // 设置序号越小优先级越高越靠前执行确保在其他业务处理之前执行到registrationBean.addUrlPatterns(/*);return registrationBean;}Bean // 确保过滤器被加入到ioc容器public CustomCorsFilter customCorsFilter(){return new CustomCorsFilter();}// 实现一个过滤器继承Filter接口覆盖doFilter方法public static class CustomCorsFilter implements Filter{Overridepublic void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {HttpServletResponse response (HttpServletResponse) servletResponse;HttpServletRequest request (HttpServletRequest) servletRequest;response.setHeader(Access-control-Allow-Origin, request.getHeader(Origin));response.setHeader(Access-Control-Allow-Methods, GET,POST,OPTIONS,PUT,DELETE,HEAD,PATCH);// 若前端传送了自定义的请求头而没有在这里面设置某些情况下则会提示跨域请求错误not allowed by Access-Control-Allow-Headers in preflight response.response.setHeader(Access-Control-Allow-Headers, x-requested-with,Authorization);response.setHeader(Access-Control-Allow-Credentials, true); // 前端请求设置credentials: include, 这里就必须设置为trueresponse.setHeader(Access-Control-Max-Age, 3600); // 设置 Access-Control-Max-Age 为 3600表示预检请求的结果可以缓存 3600 秒即 1 小时在此期间浏览器不需要为相同的跨域请求再次发送预检请求filterChain.doFilter(servletRequest,servletResponse);}}Axios文件上传 Axios是一个基于promise的http库可以用于Node.js中可兼容所有主流浏览器该库提供了简洁的API用于执行Http请求可拦截请求和响应转换请求和响应请求数据等 引入axios库然后使用axios提供的方法进行文件上传后端接收文件的方法与上述一致 !-- 引入axios库实际开发中建议用 npm install axios 安装到本地 -- script srchttps://unpkg.com/axios/dist/axios.min.js/script script function uploadFileByAxios() {const files document.getElementById(fileInput).files;const formData new FormData();for (let i 0; i files.length; i) {formData.append(files, files[i]);}let httpHeaders {}; // 表单Content-Type可以不用设置默认就是 httpHeaders[Content-Type] multipart/form-data;// let uploadUrl http://127.0.0.1:7999/file/upload;axios.post(uploadUrl, formData, { headers: httpHeaders }) .then(response {console.log(Files uploaded successfully:, response.data);}).catch(error {console.error(Error uploading files:, error);});}/scriptFetch API实现文件上传 Fetch API 是一个用于现代Web应用执行网络请求的内置接口它在XMLHttpRequest 之后出现 Fetch API 基于 Promise可以使用链式结构来处理请求和响应它提供了一种更简单、更灵活的方式来进行异步请求 function uploadFilesByFetch() {let files document.getElementById(fileInput).files;let formData new FormData();for (let i 0; i files.length; i) {formData.append(files, files[i]);}let uploadUrl uploadUrl http://127.0.0.1:7999/file/upload;fetch(uploadUrl, {method: POST,body: formData}).then(response {if (!response.ok) {throw new Error(Network response was not ok response.statusText);}return response.json(); }).then(data {console.log(Files uploaded successfully:, data);}).catch(error {console.error(Error uploading files:, error);}); }下载文件 下载文件包括交互事件请求下载、超链接下载 XMLHttpRequest文件下载 1.下载按钮事件 divlabel下载地址/labelinput typetext iddownloadUrl valuehttp://127.0.0.1:7999/file/download/filename/input /div button onclickdownloadFileByXHR()XMLHttpRequest下载/button !-- 可以隐藏一个a标签通过其他按钮事件触发 -- button onclickdownloadLink()超链接下载方式/buttona idlink styledisplay: none;/a !-- 超链接下载方法直接指定带有href属性的a标签点击即可下载 -- a hrefhttp://127.0.0.1:7999/file/download/24071411085841900.txt downloadfilename.txt点击下载文件/a2.XMLHttpRequest下载文件 关键是设置responseType以及对响应数据二进制数据处理 function downloadFileByXHR() {let downloadUrl document.getElementById(downloadUrl).value; // 下载的url地址// let downloaduUrl http://192.168.95.59:7999/download/file/fileName;let request new XMLHttpRequest();request.open(GET, downloadUrl, true);// 响应被处理为 Blob 对象可以通过 xhr.response 访问响应数据适用于处理二进制数据如文件、图像等request.responseType blob;request.onload function () {if (request.status 200) {let contentType request.getResponseHeader(Content-Type);//验证响应响应数据若异常则响应 Content-Type 为 json 认定为下载失败if (contentType contentType.startsWith(application/json)) {const reader new FileReader();reader.onload function () {const text reader.result; // 下载出错时后端返回的错误信息console.log(text:, text);};reader.readAsText(request.response); // 尝试将blob数据读取为文本数据return;}let disposition request.getResponseHeader(Content-Disposition);// Content-Disposition: attachment; filename24070701440188300.pngconst data new Blob([request.response], { type: request.getResponseHeader(Content-Type) });const url window.URL.createObjectURL(data);const link document.createElement(a);link.href url;let fileName getFileName(disposition);if (fileName ! ) {link.download fileName;}document.body.appendChild(link);link.click();document.body.removeChild(link);window.URL.revokeObjectURL(url); // 释放内存link.remove();}};request.onerror function () {console.low(error:, request.responseText);};request.send(); }链接下载方法 // 设置隐藏表单下载链接点击即可下载这种方法不够友好若后端错误了前端直接暴露原始错误信息 function downloadLink() {console.log(链接下载);let link document.getElementById(link);link.href http://127.0.0.1:7999/file/download/24071415521018500.txt;// link.download test.txt; // 可指定下载后的名称link.click(); }3.后端文件下载处理 Controller层接收文件下载请求 ApiOperation(value 下载文件)RequestMapping(value /download/{fileName}, method RequestMethod.GET)ResponseEntitybyte[] getDownloadFileResponse(PathVariable(fileName) String fileName);Service层读取文件 // 给前端响应一个字节数组使用ResponseEntity封装以便设置HttpHeaders public ResponseEntitybyte[] getDownloadFileResponse(String fileName) {byte[] imageBytes this.getLocalFile(fileName); // 获取文件的字节数组String contentType getFileMediaType(fileName).toString();try {fileName URLEncoder.encode(fileName, UTF-8); // 中文字符串编码处理避免前端收到乱码} catch (UnsupportedEncodingException e) {throw new RuntimeException(e);}HttpHeaders headers new HttpHeaders();// headers.add(Custom-Action, download);headers.add(HttpHeaders.CONTENT_TYPE, contentType); // 设置Content-Typeheaders.add(HttpHeaders.CONTENT_DISPOSITION, attachment; filename fileName); // 设置附件前端点击超链接下载时该名称将作为下载的文件名称headers.add(Access-Control-Expose-Headers, Content-Type,Content-Disposition,Custom-Action);return new ResponseEntity(imageBytes, headers, HttpStatus.OK);}// 读取磁盘指定文件为字节数组public byte[] getLocalFile(String fileName) {File file new File(localFileProperties.getPath() / fileName); // 服务器文件位置FileInputStream inputStream null;try {inputStream new FileInputStream(file);byte[] bytes new byte[inputStream.available()];inputStream.read(bytes, 0, inputStream.available());return bytes;} catch (IOException e) {e.printStackTrace();log.error(获取文件失败:,e);throw new RuntimeException(找不到指定的文件);}}// 根据文件名后缀确定图片的 MIME 类型public MediaType getFileMediaType(String imageName) {String fileExtension imageName.substring(imageName.lastIndexOf(.) 1).toLowerCase();switch (fileExtension) {case jpeg:case jpg:return MediaType.IMAGE_JPEG;case png:return MediaType.IMAGE_PNG;case gif:return MediaType.IMAGE_GIF;case bmp:return MediaType.parseMediaType(image/bmp);default:return MediaType.APPLICATION_OCTET_STREAM; // 默认为二进制流}}4.后端外链请求方法 Controller层接收一个下载外链的请求 ApiOperation(value 第三方外链下载) RequestMapping(value /outlink/download, method RequestMethod.GET) ResponseEntitybyte[] getDownloadOutlinkResponse(RequestParam(url) String url);Service层读取第三方服务器的文件 这里为啥前端不直接访问第三方文件服务器因为在一些业务场景中前端可能没有权限访问直接访问、直接访问不安全或者在对第三方外链的访问情况做分析统计时需要 public ResponseEntitybyte[] downloadOutLink(String fileUrl) {log.info(downloadOutLink fileUrl{}, fileUrl);RestTemplate restTemplate new RestTemplate();ResponseEntitybyte[] responseEntity restTemplate.exchange(fileUrl, HttpMethod.GET, null, byte[].class);byte[] bytes responseEntity.getBody();log.info(return entity...);HttpHeaders oldHeaders responseEntity.getHeaders();oldHeaders.entrySet().forEach(entry - {log.info(headName:{}, headValue:{}, entry.getKey(), entry.getValue());});String contentDisposition oldHeaders.getFirst(HttpHeaders.CONTENT_DISPOSITION);String contentType oldHeaders.getFirst(HttpHeaders.CONTENT_TYPE);HttpHeaders responseHeaders new HttpHeaders(); // 新创建一个 responseHeaders 用于设置返回前端的请求头此处没有直接使用oldHeaders因为这样可能会暴露出现某些不被允许的请求头以至于出错if (contentDisposition ! null !Objects.equals(, contentDisposition)) {responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, contentDisposition);} else {// 获取一个文件扩展名String fileSuffix ;if(fileUrl.contains(.)){String suffix fileUrl.substring(fileUrl.lastIndexOf(.)1).toLowerCase();if(suffix.length()0 suffix.length()5){fileSuffix suffix;}}if(Objects.equals(fileSuffix,)){fileSuffix download;}String fileName System.currentTimeMillis().fileSuffix;responseHeaders.add(HttpHeaders.CONTENT_DISPOSITION, String.format(attachment; filename%s,fileName));}responseHeaders.add(HttpHeaders.CONTENT_TYPE, contentType);return ResponseEntity.ok().headers(responseHeaders).body(bytes);} Fetch API下载文件 function downloadFileByFetch() {// const url http://127.0.0.1:7999/file/upload; // 文件的URLlet url document.getElementById(downloadUrl).value;fetch(url).then(async (response) {console.log(response:, response);if (!response.ok) {throw new Error(Network response was not ok response.statusText);}const contentType response.headers.get(Content-Type);// 后端抛出异常时返回json数据if(contentType contentType.startsWith(application/json)){const jsonResponse await response.json();if (jsonResponse.code 1) {// 后端返回了统一错误响应console.error(Request failed with message:, jsonResponse.msg);showInfo(JSON.stringify(jsonResponse));return;}}console.log(contentType:,contentType);const disposition response.headers.get(Content-Disposition);console.log(disposition:, disposition);let fileName unknown.download;if (disposition ! null) {const fileNameMatch disposition.match(/filename?([^])?/);if (fileNameMatch.length 2) {fileName fileNameMatch[1];}fileName fileName.replace(/\/g, );fileName decodeURI(fileName);}response.blob().then(blob {const url window.URL.createObjectURL(blob);const a document.createElement(a);a.style.display none;a.href url;a.download fileName; // 设置下载文件名document.body.appendChild(a);a.click();window.URL.revokeObjectURL(url); // 释放URL对象});}).catch(error {console.error(error:, error);}); }Axios下载文件 const downloadFileByAxios () {// 使用 Axios 发起 GET 请求// let url http://192.168.95.59:7999/download/file/xxx;let url document.getElementById(downloadUrl).value;let httpHeaders {};// httpHeaders { Custom-Action: RQ-download };axios.get(url, { responseType: blob, headers: httpHeaders }).then(response {// 请求成功处理console.log(收到下载数据:, response);// let res response.data;handleAxiosDownloadResponse(response.data, response.headers);}).catch(error {// 请求失败处理console.error(Error fetching posts:, error);}); } // 处理axios下载的响应文件 const handleAxiosDownloadResponse async (data, headers) {const contentType headers[content-type];// 后端返回的不是文件而是json对象判断为异常if (contentType contentType.startsWith(application/json)) {// data.text()返回一个Promise对象因此要使用await此时方法使用使用async关键字标识为异步方法let text await data.text();console.log(text:, text);const jsonObj JSON.parse(text);if (jsonObj.code 1) {console.log(请求异常, text);}showInfo(text);return;}const blob new Blob([data], { type: headers[content-type] });// 创建一个指向 Blob 对象的 URLconst url window.URL.createObjectURL(blob);// 创建一个 a 元素并设置其 href 属性指向 Blob URLconst a document.createElement(a);a.href url;// 从响应头或其他方式获取文件名const contentDisposition headers[content-disposition];console.log(content-disposition:, contentDisposition)let fileName ;if (contentDisposition) {const fileNameMatch contentDisposition.match(/filename?([^])?/);if (fileNameMatch.length 2) {fileName fileNameMatch[1];}fileName fileName.replace(/\/g, ); // 将符号替换为空格fileName decodeURI(fileName);// console.log(fileName:,fileName);}a.download fileName; // 设置下载文件名// 将 a 元素添加到 DOM 并触发点击事件document.body.appendChild(a);a.click();// 移除 a 元素并释放 Blob URLdocument.body.removeChild(a);window.URL.revokeObjectURL(url); }Axios读取文件内容 const readFileContentByAxios () {// 使用 Axios 发起 GET 请求// let url http://192.168.95.59:7999/download/file/xxx;let url document.getElementById(downloadUrl).value;let httpHeaders {};// responseType选项取值与XMLHttpRequest的responseType一样指定为arraybuffer表示响应被处理为 ArrayBuffer 对象适用于处理原始二进制数据axios.get(url, { responseType: arraybuffer, headers: httpHeaders }).then(async (response) {// 请求成功处理console.log(收到数据:, response);// let res response.data;const text new TextDecoder(utf-8).decode(new Uint8Array(response.data));console.log(text); }).catch(error {// 请求失败处理console.error(Error fetching posts:, error);}); }参考资料 1.XMLHttpRequest(XHR)对象 2.Fetch API接口指南 3.Axios API中文文档 4.Spring Framework文档
http://www.dnsts.com.cn/news/48805.html

相关文章:

  • 网站建设建议书wordpress会员提成插件
  • 服装网站开发方案psd网站
  • 陕西省关于网站信息内容建设wordpress文章类型查询
  • 手机网站建设的现状湖南常德石门县
  • 用什么做网站原型图哪些网站是响应式网站
  • 什么是网络营销工程师苏州谷歌seo
  • 商城网站平台怎么做网设
  • 网站开发如何收费随州网站推广哪家专业
  • 卖网站模板赚钱吗表单插件wordpress
  • 柳州网站网站建设防止网站扫描
  • 单位不能建设网站wordpress添加多语言
  • 该模板尚未授权此网站wordpress.conf
  • 宁波网站建设制作公司哪家好wordpress文章相册模式
  • 网站前端工资企业网站如何进行定位
  • 1688开山网一起做网站wordpress presscore
  • 海南网站优化公司公众号发布文章教程
  • 网站建设七大步骤站长工具在线查询
  • 公司建网站多少钱合适免费申请二级域名
  • 深圳精美网站设计怎么注册商标品牌
  • 无锡市网站搭建邓州网站制作
  • 网站好看的图标代码百度公司做网站
  • 哪些网站是百度新闻源广州网站建设兼职
  • 做彩票网站是违法的吗怎么做网站自动响应
  • 中国临沂网站优化网络运营一个月工资
  • 介休做网站长春建站网站模板
  • 网站建设开发设计公司seo培训教程
  • 单页面网站制作视频广东室内设计公司排名
  • 电脑做网站服务器改端口公司网站高端网站建设
  • 网站做小学一年二班作业怎么做网站特效模板
  • 网站建设如何上传文件网页框架模板