Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 Warning: error_log(/data/www/wwwroot/hmttv.cn/caches/error_log.php): failed to open stream: Permission denied in /data/www/wwwroot/hmttv.cn/phpcms/libs/functions/global.func.php on line 537 日本高清视频色,国产亚洲美女精品久久久,中文字幕一区二区三区免费看

          整合營(yíng)銷(xiāo)服務(wù)商

          電腦端+手機(jī)端+微信端=數(shù)據(jù)同步管理

          免費(fèi)咨詢(xún)熱線(xiàn):

          07-文件上傳到阿里云OSS實(shí)戰(zhàn)(二)

          07-文件上傳到阿里云OSS實(shí)戰(zhàn)(二)

          、創(chuàng)建Bucket

          Bucket有點(diǎn)像電腦里面的盤(pán)符或者目錄,我們文件的上傳,必須指定上傳到哪個(gè)Bucket里面。因此,在上傳之前必須創(chuàng)建它。

          在阿里云控制臺(tái)點(diǎn)擊OSS服務(wù),然后點(diǎn)擊【Bucket列表】就可以看到如下界面:

          然后點(diǎn)擊【創(chuàng)建Bucket】按鈕,按下圖填寫(xiě):

          Bucket名稱(chēng):需要自己要進(jìn)行命名,相當(dāng)于自己文件的存儲(chǔ)目錄。

          區(qū)域:這個(gè)選擇很重要,因?yàn)楹竺嬲{(diào)用api接口上傳文件的時(shí)候需要使用。

          Endpoint:這個(gè)也很重要,因?yàn)楹竺嬲{(diào)用api接口上傳文件的時(shí)候也需要使用。

          上述選擇完成后,就可以點(diǎn)擊【確定】按鈕進(jìn)行創(chuàng)建了。

          創(chuàng)建成功后,進(jìn)入Bucket信息頁(yè)面,可以點(diǎn)擊左側(cè)的【文件管理】按鈕,出現(xiàn)如下界面:

          好了,到此為止,阿里云的OSS配置就算完成了,新手對(duì)于這個(gè)過(guò)程感覺(jué)十分困惑,因此我這里說(shuō)的比較詳細(xì)。

          2、OSS流式上傳

          下面是官網(wǎng)文檔地址以及官方代碼,我摘抄了幾個(gè)入門(mén)級(jí)的代碼。

          https://help.aliyun.com/document_detail/84781.html?spm=a2c4g.11186623.6.787.101245dcJzaFge

          (1)上傳字符串

          // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
          String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
          // 阿里云主賬號(hào)AccessKey擁有所有API的訪(fǎng)問(wèn)權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號(hào)。
          String accessKeyId="<yourAccessKeyId>";
          String accessKeySecret="<yourAccessKeySecret>";
          
          // 創(chuàng)建OSSClient實(shí)例。
          OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
          
          // 創(chuàng)建PutObjectRequest對(duì)象。
          String content="Hello OSS";
          // <yourObjectName>表示上傳文件到OSS時(shí)需要指定包含文件后綴在內(nèi)的完整路徑,例如abc/efg/123.jpg。
          PutObjectRequest putObjectRequest=new PutObjectRequest("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content.getBytes()));
          
          // 如果需要上傳時(shí)設(shè)置存儲(chǔ)類(lèi)型與訪(fǎng)問(wèn)權(quán)限,請(qǐng)參考以下示例代碼。
          // ObjectMetadata metadata=new ObjectMetadata();
          // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
          // metadata.setObjectAcl(CannedAccessControlList.Private);
          // putObjectRequest.setMetadata(metadata);
          
          // 上傳字符串。
          ossClient.putObject(putObjectRequest);
          
          // 關(guān)閉OSSClient。
          ossClient.shutdown();                   

          (2)上傳Byte數(shù)組

          // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
          String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
          // 阿里云主賬號(hào)AccessKey擁有所有API的訪(fǎng)問(wèn)權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號(hào)。
          String accessKeyId="<yourAccessKeyId>";
          String accessKeySecret="<yourAccessKeySecret>";
          
          // 創(chuàng)建OSSClient實(shí)例。
          OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId,accessKeySecret);
          
          // 上傳Byte數(shù)組。
          byte[] content="Hello OSS".getBytes();
          ossClient.putObject("<yourBucketName>", "<yourObjectName>", new ByteArrayInputStream(content));
          
          // 關(guān)閉OSSClient。
          ossClient.shutdown();

          (3)上傳網(wǎng)絡(luò)流

          // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
          String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
          // 阿里云主賬號(hào)AccessKey擁有所有API的訪(fǎng)問(wèn)權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號(hào)。
          String accessKeyId="<yourAccessKeyId>";
          String accessKeySecret="<yourAccessKeySecret>";
          
          // 創(chuàng)建OSSClient實(shí)例。
          OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
          
          // 上傳網(wǎng)絡(luò)流。
          InputStream inputStream=new URL("https://www.aliyun.com/").openStream();
          ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
          
          // 關(guān)閉OSSClient。
          ossClient.shutdown();

          (4)上傳文件流

          // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
          String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
          // 云賬號(hào)AccessKey有所有API訪(fǎng)問(wèn)權(quán)限,建議遵循阿里云安全最佳實(shí)踐,創(chuàng)建并使用RAM子賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建。
          String accessKeyId="<yourAccessKeyId>";
          String accessKeySecret="<yourAccessKeySecret>";
          
          // 創(chuàng)建OSSClient實(shí)例。
          OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
          
          // 上傳文件流。
          InputStream inputStream=new FileInputStream("<yourlocalFile>");
          ossClient.putObject("<yourBucketName>", "<yourObjectName>", inputStream);
          
          // 關(guān)閉OSSClient。
          ossClient.shutdown();

          3、簡(jiǎn)單文件上傳

          // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
          String endpoint="http://oss-cn-hangzhou.aliyuncs.com";
          // 阿里云主賬號(hào)AccessKey擁有所有API的訪(fǎng)問(wèn)權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號(hào)。
          String accessKeyId="<yourAccessKeyId>";
          String accessKeySecret="<yourAccessKeySecret>";
          
          // 創(chuàng)建OSSClient實(shí)例。
          OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
          
          // 創(chuàng)建PutObjectRequest對(duì)象。
          PutObjectRequest putObjectRequest=new PutObjectRequest("<yourBucketName>", "<yourObjectName>", new File("<yourLocalFile>"));
          
          // 如果需要上傳時(shí)設(shè)置存儲(chǔ)類(lèi)型與訪(fǎng)問(wèn)權(quán)限,請(qǐng)參考以下示例代碼。
          // ObjectMetadata metadata=new ObjectMetadata();
          // metadata.setHeader(OSSHeaders.OSS_STORAGE_CLASS, StorageClass.Standard.toString());
          // metadata.setObjectAcl(CannedAccessControlList.Private);
          // putObjectRequest.setMetadata(metadata);
          
          // 上傳文件。
          ossClient.putObject(putObjectRequest);
          
          // 關(guān)閉OSSClient。
          ossClient.shutdown();            

          4、通過(guò)自定義WEB接口上傳文件

          首先我們來(lái)定一個(gè)阿里云OSS文件上傳的一個(gè)工具類(lèi),具體代碼如下:

          package com.shenmazong.utils;
          
          import com.aliyun.oss.OSS;
          import com.aliyun.oss.OSSClientBuilder;
          import com.aliyun.oss.model.PutObjectResult;
          import org.springframework.web.multipart.MultipartFile;
          
          import java.io.IOException;
          import java.util.UUID;
          
          public class UploadFileUtils {
          
              /**
               * TODO uploadAliyunOss 阿里云OSS文件上傳
               * @param file
               * @param fileName
               * @return
               */
              public static String uploadAliyunOss(MultipartFile file, String fileName) {
                  String url=null;
                  // Endpoint以杭州為例,其它Region請(qǐng)按實(shí)際情況填寫(xiě)。
                  String endpoint="http://oss-cn-beijing.aliyuncs.com";
                  // 阿里云主賬號(hào)AccessKey擁有所有API的訪(fǎng)問(wèn)權(quán)限,風(fēng)險(xiǎn)很高。強(qiáng)烈建議您創(chuàng)建并使用RAM賬號(hào)進(jìn)行API訪(fǎng)問(wèn)或日常運(yùn)維,請(qǐng)登錄 https://ram.console.aliyun.com 創(chuàng)建RAM賬號(hào)。
          
                  // shenmazong@1642590691341298.onaliyun.com
                  String accessKeyId="XXXX";
                  String accessKeySecret="XXXX";
          
                  // 創(chuàng)建OSSClient實(shí)例。
                  OSS ossClient=new OSSClientBuilder().build(endpoint, accessKeyId, accessKeySecret);
          
                  // 創(chuàng)建PutObjectRequest對(duì)象。
                  // 上傳文件流。
                  PutObjectResult result=null;
                  String newName=UUID.randomUUID().toString();
                  try {
                      result=ossClient.putObject("shenmazong", fileName, file.getInputStream());
                      url="https://shenmazong.oss-cn-beijing.aliyuncs.com/" + fileName;
                  } catch (IOException e) {
                      e.printStackTrace();
                      return null;
                  }
          
                  // 關(guān)閉OSSClient。
                  ossClient.shutdown();
                  return url;
              }
          }

          上面的工具類(lèi),是傳輸web上傳的文件流,然后返回能夠訪(fǎng)問(wèn)的url,這樣我們web網(wǎng)上實(shí)現(xiàn)圖片文件上傳,就很容易了。

          下面我們就可以編寫(xiě)service來(lái)實(shí)現(xiàn)我們的文件上傳接口了。

              /**
               * TODO 實(shí)現(xiàn)用戶(hù)頭像的上傳
               * @param userId
               * @param file
               * @return
               */
              @Override
              public ResponseResult uploadHead(Integer userId, MultipartFile file) {
                  ResponseResult result=ResponseResult.SUCCESS();
          
                  //--1 驗(yàn)證用戶(hù)是否存在
                  TbUser user=iTbUserMapper.selectById(userId);
                  if(user==null) {
                      result.setCode(-1);
                      result.setMessage("獲取用戶(hù)信息失敗");
                      return result;
                  }
          
                  //--2 上傳文件
                  if(file.isEmpty()){
                      log.info("上傳文件為空");
                      result.setCode(-1);
                      result.setMessage("上傳文件為空");
                      return result;
                  }
                  String fileName=file.getOriginalFilename();
                  int size=(int) file.getSize();
                  log.info(fileName + "-->" + size);
          
                  UUID uuid=UUID.randomUUID();
                  String suffix=fileName.substring(fileName.lastIndexOf(".")).toLowerCase();
                  String newName=uuid.toString() + suffix;
                  String headUrl=UploadFileUtils.uploadAliyunOss(file, newName);
                  if(headUrl==null) {
                      result.setCode(-1);
                      result.setMessage("上傳文件到OSS失敗");
                      return result;
                  }
          
                  //--3 修改用戶(hù)頭像鏈接
                  user.setHeadurl(headUrl);
                  iTbUserMapper.updateById(user);
          
                  return result;
              }

          service寫(xiě)好以后,就需要編寫(xiě)controller了,具體代碼如下:

              /**
               * TODO uploadHead 上傳頭像圖片
               * @param userId
               * @param file
               * @return
               */
              @ApiOperation(value="頭像上傳", notes="用戶(hù)上傳更新自己的頭像")
              @ApiImplicitParams(value={
                      @ApiImplicitParam(name="userId", value="用戶(hù)ID", required=true, dataType="int", example="0")
              })
              @PostMapping(value="/uploadHead", headers="content-type=multipart/form-data")
              public ResponseResult uploadHead(@RequestParam("userId") Integer userId,
                                       @RequestParam("uploadFile") MultipartFile file) {
                  return userService.uploadHead(userId, file);
              }

          最后是html頁(yè)面的示例代碼,也一并貼上來(lái):

          <!DOCTYPE html>
          <html>
          <head>
              <meta charset="utf-8">
              <title></title>
              <!-- 最新版本的 Bootstrap 核心 CSS 文件 -->
              <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
          
              <!-- 可選的 Bootstrap 主題文件(一般不用引入) -->
              <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap-theme.min.css" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
          
              <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js" type="text/javascript" charset="utf-8"></script>
              <!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
              <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
          </head>
          <body>
          <div class="container">
              <div class="row">
                  <form action="upload" method="post" enctype="multipart/form-data">
                      <div class="form-group">
                          <label for="uploadFile">選擇文件</label>
                          <input type="file" id="uploadFile" name="uploadFile">
                          <p class="help-block">Example block-level help text here.</p>
                      </div>
                      <button type="submit" class="btn btn-primary">上傳</button>
                  </form>
              </div>
          </div>
          </body>
          </html>
          

          最后的實(shí)例演示,是我在一個(gè)演示項(xiàng)目中拷貝過(guò)來(lái)的代碼,沒(méi)有針對(duì)咱們這個(gè)教程進(jìn)行整理,但是作為參考資料,這部分代碼是合格的。

          018年初,一個(gè)物理專(zhuān)業(yè)的學(xué)生Jan B?hmer創(chuàng)建了一個(gè)網(wǎng)站,用來(lái)跟蹤和記錄用戶(hù)的點(diǎn)擊、鼠標(biāo)移動(dòng)、瀏覽器類(lèi)型和操作系統(tǒng)等數(shù)據(jù)。雖然用戶(hù)跟蹤并不新鮮,但他的方法不需要JavaScript、插件或外部庫(kù)。實(shí)際上,它只使用了普通HTML文本和一點(diǎn)CSS。

          它是如何工作的

          B?hmer的概念利用了CSS的兩個(gè)特性:將內(nèi)容注入HTML元素的能力,以及在用戶(hù)執(zhí)行操作后更改樣式的能力。網(wǎng)站的工作方式是在執(zhí)行操作時(shí)使用content屬性設(shè)置URL。此URL調(diào)用一個(gè)腳本,該腳本記錄有關(guān)操作的詳細(xì)信息,這些操作作為URL參數(shù)進(jìn)行傳遞。使用::before和::after CSS選擇器設(shè)置這個(gè)URL可以確保只在執(zhí)行操作時(shí)調(diào)用URL,而不是在頁(yè)面首次加載時(shí)調(diào)用URL。

          例如,下面的CSS在每次單擊#link元素時(shí)調(diào)用一次URL:

          跟蹤腳本包含記錄事件和操作執(zhí)行次數(shù)的代碼。它還可以用于提取用戶(hù)的IP地址、用戶(hù)代理和其他標(biāo)識(shí)信息。

          下面是這樣一個(gè)腳本在PHP中的示例:

          檢測(cè)瀏覽器類(lèi)型

          用戶(hù)可以欺騙瀏覽器的用戶(hù)代理,但是 B?hmer繞過(guò)了這個(gè)問(wèn)題,他使用@supports at-rule(at-rule 是CSS樣式聲明,以@開(kāi)頭,緊跟著是標(biāo)識(shí)符(charset),最后以分號(hào)(;)結(jié)尾。)測(cè)試瀏覽器特定的CSS屬性。例如,下面的操作通過(guò)檢測(cè)--webkit-appearance是可用的,而-ms-ime-align是不可用的來(lái)檢測(cè)Chrome瀏覽器:

          檢測(cè)操作系統(tǒng)

          B?hmer甚至使用字體檢測(cè)來(lái)識(shí)別用戶(hù)的操作系統(tǒng)。例如,通過(guò)檢測(cè)瀏覽器是否支持Calibri字體家族,我們可以假定瀏覽器運(yùn)行在Windows中:

          B?hmer關(guān)于此概念的驗(yàn)證可以識(shí)別其他數(shù)據(jù)點(diǎn),包括瀏覽器窗口的大小和方向、用戶(hù)是否單擊了鏈接以及用戶(hù)在一個(gè)元素上停留的時(shí)間。

          這種攻擊在瀏覽器中非常難以預(yù)防。完全防止它的唯一方法就是禁用CSS,這會(huì)使網(wǎng)站無(wú)法使用。然而,通過(guò)使用內(nèi)容安全策略(CSP),可以減少攻擊者利用此漏洞的機(jī)會(huì)。

          使用內(nèi)容安全策略減少CSS泄漏

          CSP是一組規(guī)則,它決定瀏覽器可以執(zhí)行哪些操作,不能執(zhí)行哪些操作。CSP通常用于防止跨站腳本攻擊(XSS)和由瀏覽器加載不信任腳本導(dǎo)致的其他攻擊。雖然CSP通常用于JavaScript文件,但它也可以應(yīng)用于CSS樣式和樣式表。

          考慮一個(gè)使用第三方提供商托管的樣式表的網(wǎng)站。攻擊者破壞樣式表并將用戶(hù)跟蹤代碼添加到頁(yè)面上的鏈接:

          當(dāng)用戶(hù)點(diǎn)擊該鏈接時(shí),他們的瀏覽器調(diào)用evil.com上托管的跟蹤腳本。由于這完全是通過(guò)瀏覽器完成的,網(wǎng)站所有者完全不知道這個(gè)漏洞。

          Content-Security-Policy通過(guò)設(shè)置允許哪些樣式以及樣式來(lái)源等規(guī)則來(lái)防止這種情況。

          禁用內(nèi)聯(lián)樣式

          禁用內(nèi)聯(lián)樣式是CSP提供的最大安全好處之一。內(nèi)聯(lián)樣式是直接在HTML文檔中聲明的樣式(或使用JavaScript設(shè)置的樣式),而不是從樣式表加載的樣式。內(nèi)聯(lián)樣式——尤其是動(dòng)態(tài)生成的樣式或用戶(hù)創(chuàng)建的樣式——非常難以保護(hù)。這就是為什么CSP通常會(huì)鎖定所有內(nèi)聯(lián)腳本和樣式,并將那些已被特別批準(zhǔn)的內(nèi)聯(lián)腳本和樣式列入白名單。

          以下規(guī)則將阻止所有內(nèi)聯(lián)樣式以及外部托管的樣式表:

          使用Hash和Nonce驗(yàn)證樣式

          如果阻塞內(nèi)聯(lián)樣式是不可行的,你仍然可以使用hash和nonce來(lái)確保CSS的完整性。

          Hash是由一個(gè)文件或字符串的內(nèi)容生成的單向字符串。在樣式表或內(nèi)聯(lián)樣式上執(zhí)行哈希函數(shù)時(shí),除非樣式發(fā)生改變,否則它總是返回相同的結(jié)果。這對(duì)于將某些內(nèi)聯(lián)樣式和樣式表加入白名單是很有用的,只需要同時(shí)驗(yàn)證樣式?jīng)]有被修改或篡改。

          Nonce的功能與hash類(lèi)似。使用nonce,將為每個(gè)請(qǐng)求生成一個(gè)新的隨機(jī)數(shù),這使得攻擊者更難猜測(cè)它的值。這避免了hash的一個(gè)關(guān)鍵缺點(diǎn),即多個(gè)輸入可能生成相同的hash值(稱(chēng)為沖突)。

          驗(yàn)證外部托管的樣式表

          樣式表通常托管在第三方服務(wù)器上,如內(nèi)容交付網(wǎng)絡(luò)(content delivery networks, CDNs),但這帶來(lái)了新的攻擊方向。如果CDN受到威脅,如何阻止攻擊者用自己修改過(guò)的版本替換樣式表?子資源完整性,也叫SRI,試圖解決這個(gè)問(wèn)題。

          SRI使用hash值來(lái)驗(yàn)證腳本和樣式表的內(nèi)容。計(jì)算每個(gè)文件的hash值,并將其附加到HTML元素的integrity屬性中。當(dāng)瀏覽器下載腳本或樣式表時(shí),計(jì)算其hash值并將其與存儲(chǔ)在屬性中的值進(jìn)行比較。如果匹配,瀏覽器將加載腳本或樣式。

          這是在假設(shè)web頁(yè)面是從受信任的源(如源服務(wù)器)提交的情況下運(yùn)行的,而當(dāng)資源是從不受信任的源(如第三方)提交的時(shí)候,就無(wú)法正常運(yùn)行。如果web頁(yè)面和資源都由第三方托管,攻擊者只需要簡(jiǎn)單地修改web頁(yè)面來(lái)匹配其CSS替換文件的hash值即可。

          結(jié)論

          雖然通過(guò)CSS跟蹤用戶(hù)的能力并不新鮮,但它確實(shí)要求我們以不同的方式考慮網(wǎng)頁(yè)上的隱私和安全性。CSS是現(xiàn)代網(wǎng)頁(yè)的基本語(yǔ)言之一,禁用網(wǎng)站的CSS將使網(wǎng)頁(yè)的大部分內(nèi)容無(wú)法使用。內(nèi)容安全策略是阻止XSS攻擊和CSS泄漏的最佳方法。Templarbit創(chuàng)建了一個(gè)“靈活的內(nèi)容-安全-策略工作流”,以便于維護(hù)CSP頭文件。如果你的團(tuán)隊(duì)正在努力為你的應(yīng)用程序推出CSP,請(qǐng)立即注冊(cè)一個(gè)免費(fèi)試用版,并學(xué)習(xí)更多關(guān)于Templarbit如何解決CSS泄露的方法.

          你可以在GitHub上找到B?hmer的概念驗(yàn)證的源代碼。

          相關(guān)連接:

          網(wǎng)站——http://crookedss.bplaced.net/

          最大的安全好處——https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src#Unsafe_inline_styles

          通常鎖定所有內(nèi)聯(lián)腳本——https://developers.google.com/web/fundamentals/security/csp/

          加入白名單——https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/style-src

          并不新鮮——https://www.smashingmagazine.com/2014/10/css-only-solution-for-ui-tracking/

          現(xiàn)在就注冊(cè)獲取免費(fèi)版——https://www.templarbit.com/signup

          GitHub——https://github.com/jbtronics/CrookedStyleSheets

          相關(guān)知識(shí):

          SRI,Subresource Integrity 的縮寫(xiě),中文:子資源完整性,由 Web 應(yīng)用安全工作組(Web Application Security Working Group)發(fā)布。

          英文原文:https://www.templarbit.com/blog/2018/03/20/tracking-users-with-css/
          譯者:憂(yōu)郁的紅秋褲

          互聯(lián)網(wǎng)上有很多資源可以找到關(guān)于機(jī)器學(xué)習(xí)數(shù)據(jù)集的見(jiàn)解和訓(xùn)練模型,但是關(guān)于如何使用這些模型構(gòu)建實(shí)際應(yīng)用程序的文章很少。

          因此,今天我們將通過(guò)首先使用hackathon中的數(shù)據(jù)集來(lái)訓(xùn)練視頻游戲銷(xiāo)售預(yù)測(cè)模型,然后使用經(jīng)過(guò)訓(xùn)練的模型來(lái)創(chuàng)建一個(gè)基本應(yīng)用程序,根據(jù)用戶(hù)輸入為我們提供銷(xiāo)售預(yù)測(cè)來(lái)學(xué)習(xí)此過(guò)程。

          本文分為多個(gè)部分,你可以一個(gè)接一個(gè)地學(xué)習(xí),而不必一口氣完成它。從我第一次選擇數(shù)據(jù)集以來(lái),我花了整整一周的時(shí)間來(lái)完成應(yīng)用程序。因此,請(qǐng)花時(shí)間專(zhuān)注于學(xué)習(xí)構(gòu)建應(yīng)用程序的各個(gè)方面,而不是只注意最終產(chǎn)品。

          第1部分:生成模型

          我們將使用在Machine Hack網(wǎng)站上運(yùn)行的視頻游戲銷(xiāo)售預(yù)測(cè)hackathon中的數(shù)據(jù)集。首先,在MachineHack上創(chuàng)建一個(gè)帳戶(hù),然后在此鏈接上注冊(cè)hackathon。

          • https://www.machinehack.com/hackathons/video_game_sales_prediction_weekend_hackathon_10

          注冊(cè)后,轉(zhuǎn)到數(shù)據(jù)標(biāo)簽并下載zip文件,該文件將包含三個(gè)文件,即訓(xùn)練,測(cè)試和樣品提交。

          下一步將在Google Colab Notebook中介紹,你可以從以下鏈接打開(kāi)和克隆該Notebook:

          • https://colab.research.google.com/drive/1tc9I7bxLJWCAEnqyPVi1Y2nIRLz3hNxR?usp=sharing

          或者如果你想在本地或其他平臺(tái)上下載并運(yùn)行該Notebook,請(qǐng)從以下GitHub鏈接下載該Notebook:Jupyter Notebook鏈接

          • https://github.com/codeclassifiers/video-game-sales-prediction-jupyter-notebook/blob/master/Video_Game_Sales_prediction.ipynb

          Notebook電腦的第一部分簡(jiǎn)要介紹了問(wèn)題陳述。通過(guò)運(yùn)行下面顯示的下一個(gè)代碼單元,上傳我們收到的文件

          from google.colab import files
          uploaded=files.upload()
          for fn in uploaded.keys():
            print('User uploaded file "{name}" with length {length} bytes'.format(
                name=fn, length=len(uploaded[fn])))
          

          在下一個(gè)代碼單元中,我們導(dǎo)入所需的python包。它們中的大多數(shù)已預(yù)先安裝在Google Colab中,因此無(wú)需安裝它們中的任何一個(gè)。

          因?yàn)槲覀儫o(wú)法在hackathon結(jié)束后提交測(cè)試數(shù)據(jù)進(jìn)行評(píng)估,因此本文其余部分僅將數(shù)據(jù)用于Train.csv。請(qǐng)記住,Train.csv的行數(shù)少于通常用于正確訓(xùn)練模型的行數(shù)。但是,出于學(xué)習(xí)目的,我們可以使用行數(shù)較少的數(shù)據(jù)集。

          現(xiàn)在讓我們深入研究解決此機(jī)器學(xué)習(xí)問(wèn)題…

          步驟1識(shí)別目標(biāo)和獨(dú)立特征

          首先,讓我們將Train.csv導(dǎo)入pandas數(shù)據(jù)框中,然后運(yùn)行df.head()以查看數(shù)據(jù)集中的列。

          從數(shù)據(jù)框中,我們可以看到目標(biāo)列是SalesInMillions,其余列是獨(dú)立特征

          步驟2清理資料集

          首先,我們通過(guò)運(yùn)行input.isnull().sum()命令檢查null值。

          input.isnull().sum()
          #Output:
          ID                 0
          CONSOLE            0
          YEAR               0
          CATEGORY           0
          PUBLISHER          0
          RATING             0
          CRITICS_POINTS     0
          USER_POINTS        0
          SalesInMillions    0
          dtype: int64
          

          我們可以看到數(shù)據(jù)集中沒(méi)有空值。接下來(lái),我們可以通過(guò)運(yùn)行以下命令刪除不必要的列ID,因?yàn)樗谀繕?biāo)銷(xiāo)售中不起作用:input=input.drop(columns=['ID'])

          接下來(lái),我們可以使用train_test_split命令將數(shù)據(jù)框分為訓(xùn)練和測(cè)試數(shù)據(jù)集:

          train, test=train_test_split(input, test_size=0.2, random_state=42, shuffle=True)
          

          步驟3探索性數(shù)據(jù)分析

          描述性統(tǒng)計(jì)信息

          使用df.shape命令我們可以找到數(shù)據(jù)集中的總行數(shù),并且可以使用命令df.nunique()在每個(gè)列中查找唯一值。

          CONSOLE              17
          YEAR                 23
          CATEGORY             12
          PUBLISHER           184
          RATING                6
          CRITICS_POINTS     1499
          USER_POINTS        1877
          SalesInMillions    2804
          

          在EDA部分中,我們使用pandas profiling和matplotlib包生成各種列的圖形,并觀察它們與目標(biāo)列的關(guān)系。

          從EDA獲得的一些見(jiàn)解是:

          PS3平臺(tái)的銷(xiāo)售額最高。之后是Xbox360:

          動(dòng)作類(lèi)別的銷(xiāo)售額最高,難題類(lèi)別的銷(xiāo)售額最低

          2007年到2011年的銷(xiāo)售額最高:

          通常,我們?cè)贓DA之后進(jìn)行特征工程或特征選擇步驟。但是,我們的功能較少,著重于實(shí)際使用模型。因此,我們正在朝著下一步邁進(jìn)。但是,請(qǐng)記住,USER_POINTS和CRITICS_POINTS列可用于派生其他功能。

          步驟4建立模型

          由于我們具有許多分類(lèi)功能,因此我們將對(duì)數(shù)據(jù)集使用catboost回歸模型。由于catboost可以直接作用于分類(lèi)特征,因此跳過(guò)了對(duì)分類(lèi)特征進(jìn)行標(biāo)簽編碼的步驟。

          首先,我們使用pip install命令安裝catboost軟件包。

          然后,我們創(chuàng)建一個(gè)分類(lèi)特征列表,將其傳遞給模型,然后將模型擬合到訓(xùn)練數(shù)據(jù)集上:

          import catboost as cat
          cat_feat=['CONSOLE','CATEGORY', 'PUBLISHER', 'RATING']
          features=list(set(train.columns)-set(['SalesInMillions']))
          target='SalesInMillions'
          model=cat.CatBoostRegressor(random_state=100,cat_features=cat_feat,verbose=0)
          model.fit(train[features],train[target])
          

          步驟5檢查模型的準(zhǔn)確性

          首先,我們根據(jù)測(cè)試數(shù)據(jù)集創(chuàng)建真實(shí)的預(yù)測(cè):

          y_true=pd.DataFrame(data=test[target], columns=['SalesInMillions'])
          test_temp=test.drop(columns=[target])
          

          接下來(lái),我們?cè)跍y(cè)試數(shù)據(jù)集上運(yùn)行訓(xùn)練良好的模型以獲取模型預(yù)測(cè)并檢查模型準(zhǔn)確性

          y_pred=model.predict(test_temp[features])
          from sklearn.metrics import mean_squared_error
          from math import sqrt
          
          rmse=sqrt(mean_squared_error(y_true, y_pred))
          print(rmse)
          #Output: 1.5555409360901584
          

          我們的RMSE值為1.5,這相當(dāng)不錯(cuò)。有關(guān)在出現(xiàn)回歸問(wèn)題時(shí)準(zhǔn)確性指標(biāo)的更多信息,可以參考本文。

          • https://medium.com/analytics-vidhya/regression-and-performance-metrics-accuracy-precision-rmse-and-what-not-223348cfcafe

          如果你想進(jìn)一步改善模型或嘗試組合各種模型,可以在本文中參考本次hackathon獲勝者的方法:

          • https://analyticsindiamag.com/meet-the-machinehack-champions-who-cracked-the-video-game-sales-prediction-hackathon/

          步驟6將模型保存到pickle文件中

          現(xiàn)在,我們可以將模型保存到pickle文件中,然后將其保存在本地:

          import pickle
          filename='finalized_model.sav'
          pickle.dump(model, open(filename, 'wb'))
          

          保存pickle文件后,你可以從Google Colab Notebook文件部分的左側(cè)邊欄中下載并保存在本地

          額外提示

          • 添加更多數(shù)據(jù)

          我們可以通過(guò)向模型添加更多數(shù)據(jù)來(lái)改善模型預(yù)測(cè)。我們可以使用一些在Kaggle上的相關(guān)的數(shù)據(jù)集。Kaggle:https://www.kaggle.com/gregorut/videogamesales

          • 提高模型效率

          我們可以使用組合模型的堆棧來(lái)進(jìn)一步提高模型效率。

          如果你已完成此步驟,請(qǐng)輕拍一下自己的背,因?yàn)槲覀儎倓偼瓿闪隧?xiàng)目的第一個(gè)主要部分。休息一會(huì)兒,拉伸一下,然后開(kāi)始本文的下一部分。

          第2部分:根據(jù)模型創(chuàng)建后端API

          我們將使用Python Flask創(chuàng)建后端API。

          因此,首先在本地創(chuàng)建一個(gè)名為server的文件夾。另外,如果還沒(méi)有,請(qǐng)?jiān)谀愕挠?jì)算機(jī)上安裝Python和pip軟件包管理器。

          接下來(lái),我們需要在文件夾中創(chuàng)建一個(gè)虛擬環(huán)境。我在Linux上使用python3,因此我創(chuàng)建虛擬環(huán)境的命令為:python3 -m venv server。

          你可以在本文中查看適用于你的OS和Python版本的命令:Python venv:https://packaging.python.org/guides/installing-using-pip-and-virtual-environments/

          接下來(lái),我們將通過(guò)運(yùn)行以下命令激活虛擬環(huán)境: source server/bin/activate

          完成后,我們需要安裝Flask pip軟件包: pip install -U Flask

          接下來(lái),使用你喜歡的文本編輯器在服務(wù)器文件夾中創(chuàng)建一個(gè)名為“app.py”的文件,并添加以下代碼以創(chuàng)建基本的API:

          from flask import Flask, jsonify, make_response, request, abort
          app=Flask(__name__)
          @app.route("/")
          def hello():
            return "Hello World!"
          if __name__=="__main__":
            app.run()
          

          現(xiàn)在打開(kāi)終端并運(yùn)行python3 app.py以啟動(dòng)服務(wù)器。這將主要在5000端口上啟動(dòng)服務(wù)器。為了測(cè)試API,請(qǐng)?jiān)跒g覽器中打開(kāi)此鏈接:http://localhost:5000/

          你應(yīng)該在瀏覽器中打印出Hello World。如果不是,則在啟動(dòng)API時(shí)檢查API是否在其他端口上運(yùn)行或在終端上打印錯(cuò)誤。

          我們將調(diào)用POST API,因此最好在繼續(xù)進(jìn)行之前安裝Postman工具。使用此工具將向服務(wù)器發(fā)送POST請(qǐng)求。

          接下來(lái),我們需要使用以下命令安裝catboost,pandas和Flask-Cors pip軟件包:

          pip install catboost pandas Flask-Cors

          接下來(lái),將我們?cè)诘?部分末尾下載的經(jīng)過(guò)訓(xùn)練的模型的pickle文件(finalized_model.sav)復(fù)制到服務(wù)器文件夾中。

          現(xiàn)在,使用以下代碼更新 app.py:

          from flask import Flask, jsonify, make_response, request, abort
          import pandas as pd
          import catboost
          import pickle
          from flask_cors import CORS,cross_origin
          model=pickle.load(open( "finalized_model.sav", "rb"))
          app=Flask(__name__)
          app.config['CORS_HEADERS']='Content-Type'
          cors=CORS(app)
          @app.errorhandler(404)
          
          def not_found(error):
              return make_response(jsonify({'error': 'Not found'}), 404)
          
          @app.route("/")
          def hello():
            return "Hello World!"
          
          @app.route("/get_prediction", methods=['POST','OPTIONS'])
          @cross_origin()
          def get_prediction():
              if not request.json:
                  abort(400)
              df=pd.DataFrame(request.json, index=[0])
              cols=["CONSOLE","RATING","CRITICS_POINTS","CATEGORY","YEAR","PUBLISHER","USER_POINTS"]
              df=df[cols]
              return jsonify({'result': model.predict(df)[0]}), 201
          
          if __name__=="__main__":
            app.run()
          

          在第6行中,我們將訓(xùn)練后的模型導(dǎo)入到我們的python文件中。

          在第10行,我們初始化CORS模塊以允許來(lái)自客戶(hù)端API調(diào)用的請(qǐng)求。

          在第11行,我們定義了一個(gè)錯(cuò)誤處理程序,如果從服務(wù)器訪(fǎng)問(wèn)了任何未處理的異常或未定義的路徑,它將發(fā)送錯(cuò)誤響應(yīng)。

          對(duì)我們來(lái)說(shuō),主要的興趣點(diǎn)是從第19行定義的get_prediction POST API。get_prediction方法是我們從客戶(hù)端獲取數(shù)據(jù)并提供響應(yīng)的銷(xiāo)售預(yù)測(cè)。

          在第24行,我們將來(lái)自API請(qǐng)求的數(shù)據(jù)轉(zhuǎn)換為pandas數(shù)據(jù)框。現(xiàn)在,我們的模型期望列以特定順序提供正確的響應(yīng)。因此,在第25行中,我們指定列順序。在接下來(lái)的步驟中,以所需順序重新排列列。

          在第27行,model.predict用于從模型中獲取預(yù)測(cè),并將其作為響應(yīng)傳遞給客戶(hù)端。在這一步,我們準(zhǔn)備好在本地使用該API。我們可以通過(guò)發(fā)送POST-API調(diào)用來(lái)測(cè)試Postman客戶(hù)端中的API,如截圖所示:

          你可以在上述請(qǐng)求的正文部分中添加一個(gè)JSON示例,可以在代碼Github-gist中找到。

          • https://gist.github.com/codeclassifiers/67ea0145b1626984af6619e8a301c067

          確保在主體和主體類(lèi)型中選擇raw和JSON選項(xiàng)(如屏幕截圖所示),并在請(qǐng)求類(lèi)型中選擇POST。

          如果在此步驟之前一切正常,那么恭喜你,你現(xiàn)在有了一個(gè)后端API,該API可根據(jù)輸入?yún)?shù)根據(jù)經(jīng)過(guò)訓(xùn)練的模型進(jìn)行預(yù)測(cè)。

          額外提示

          • 模塊化代碼

          在后端設(shè)計(jì)中不建議在單個(gè)文件中編寫(xiě)API,我們可以將路徑和模型導(dǎo)入分隔到不同的文件夾中,以使代碼更具模塊化。如果將來(lái)引入其他API,這也將使我們能夠以可管理的方式擴(kuò)展代碼。

          在這一點(diǎn)上,我們可以再次休息一下,確保收藏了本文,以便輕松地重新開(kāi)始本項(xiàng)目的下一部分。

          第3部分:將后端API部署到Heroku

          到目前為止,我們的API在本地工作,但我們需要將其部署在遠(yuǎn)程服務(wù)器上,以供其他地方使用。為此,我們將使用Heroku作為我們的API托管平臺(tái)。

          我主要參考了來(lái)自stackabuse 的文章,將其部署到Heroku。我們將簡(jiǎn)要介紹這些步驟,但是如果你卡在了其中某個(gè)步驟,請(qǐng)?jiān)诖颂巺㈤喸嘉恼拢?/p>

          • Stackabuse文章:https://stackabuse.com/deploying-a-flask-application-to-heroku/

          首先,我們使用terminal命令安裝gunicorn:

          pip install gunicorn

          接下來(lái),運(yùn)行下面的命令將所有已安裝的pip包,存儲(chǔ)到require.txt文件:

          pip freeze > requirements.txt

          你可以參考此處上傳的requirements.txt文件以供參考:

          • Github鏈接:https://github.com/codeclassifiers/video-game-sales-prediction-backend/blob/master/requirements.txt

          接下來(lái),Procfile使用以下代碼在服務(wù)器文件夾中創(chuàng)建一個(gè)名稱(chēng)為Procfile的文件:

          web: gunicorn app:app

          現(xiàn)在,在Heroku網(wǎng)站上注冊(cè),在該網(wǎng)站上創(chuàng)建一個(gè)應(yīng)用,然后按照原始文章中的說(shuō)明安裝Heroku CLI 。

          接下來(lái),通過(guò)運(yùn)行以下命令從本地終端登錄Heroku: heroku login -i

          使用以下命令添加你的Heroku應(yīng)用git參考:

          heroku git:remote -a {your-project-name}

          現(xiàn)在,使用以下命令將代碼推送到Heroku:

          git push heroku master

          在運(yùn)行上述命令的最后,你將在終端輸出中獲得API URL,現(xiàn)在我們可以使用該URL從客戶(hù)端進(jìn)行調(diào)用。此時(shí),我們還可以從PostMan應(yīng)用程序發(fā)送API請(qǐng)求,以查看我們是否正確收到了與步驟2末尾所述類(lèi)似的響應(yīng)。

          到目前為止的代碼庫(kù)可以在以下Github存儲(chǔ)庫(kù)中找到

          • Videogame Sales Backend:https://github.com/codeclassifiers/video-game-sales-prediction-backend

          現(xiàn)在,我們?cè)诜?wù)器上托管了一個(gè)正常工作的API。如果一切正常,那么我們可以繼續(xù)開(kāi)發(fā)客戶(hù)端應(yīng)用程序。如果遇到任何問(wèn)題,請(qǐng)?jiān)谠u(píng)論部分中提及你的問(wèn)題。

          第4部分:使用react和bootstrap創(chuàng)建客戶(hù)端應(yīng)用程序

          我們將需要在我們的計(jì)算機(jī)上正確安裝和設(shè)置Node.js。因此,請(qǐng)先下載并安裝適用于你相關(guān)操作系統(tǒng)和系統(tǒng)的Node.js,然后再繼續(xù)進(jìn)行操作。另外,建議安裝yarn管理器:yarn 安裝

          現(xiàn)在,在上一步中創(chuàng)建frontend的服務(wù)器文件夾外部創(chuàng)建一個(gè)新文件夾,并從終端進(jìn)入該frontend文件夾內(nèi)部。

          接下來(lái),我們將創(chuàng)建一個(gè)新的react應(yīng)用程序,并通過(guò)在終端中運(yùn)行以下命令來(lái)啟動(dòng)它:

          npx create-react-app sales-prediction-app
          cd sales-prediction-app
          npm start
          

          你應(yīng)該在瀏覽器中看到默認(rèn)打開(kāi)的瀏覽器選項(xiàng)卡,以及react.js默認(rèn)模板應(yīng)用程序。現(xiàn)在我們需要在我們最喜歡的編輯器中打開(kāi)該項(xiàng)目(我正在使用VSCode),并開(kāi)始進(jìn)行更改以構(gòu)建前端應(yīng)用程序。

          首先,我們需要在應(yīng)用程序公共文件夾中的index.html文件中導(dǎo)入相關(guān)的引導(dǎo)程序文件。

          我們需要按照bootstrap文檔提供的說(shuō)明在index.html文件中添加文件,如下所示:

          • bootstrap文檔:https://getbootstrap.com/docs/4.5/getting-started/introduction/
          <head>
          ...
          <link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous">
          ...
          </head>
          <body>
          ...
          <div id="root"></div>
          ...
          <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
          <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
          <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
          ...
          </body>
          

          我們的最終用戶(hù)界面是集合下拉菜單項(xiàng),其中單個(gè)項(xiàng)如下所示:

          我們將在src文件夾中創(chuàng)建一個(gè)名稱(chēng)為optionsSources.json的JSON文件。JSON文件中的每個(gè)條目都包含以下對(duì)象:

          {
              "CONSOLE": {
                  "options": [
                      "ps2","x360","ps3","pc"
                  ],
                  "icon": "?",
                  "dropDownPlaceholder": "Select Console"
              }
          }
          

          下拉菜單中顯示的選項(xiàng)位于options數(shù)組中,下拉菜單選項(xiàng)左側(cè)顯示的圖標(biāo)和標(biāo)簽位于icon和dropDownPlaceholder項(xiàng)。我們需要?jiǎng)?chuàng)建多個(gè)這樣的下拉列表,因此要添加的完整JSON文件如以下文件所示:

          • https://github.com/codeclassifiers/video-salesprediction-frontend/blob/master/src/optionsSources.json

          接下來(lái),我們需要在我們的應(yīng)用程序中實(shí)現(xiàn)下拉組件。在src文件夾中創(chuàng)建一個(gè)名為components的文件夾,并在components文件夾中創(chuàng)建一個(gè)名為OptionSelection.js的文件

          我們將編寫(xiě)一個(gè)功能組件,該組件返回一個(gè)下拉項(xiàng),如下所示:

          import React,{ useState } from 'react';
          import optionSources from  '../optionsSources.json';
          function OptionSelection({itemKey, setOptionInObject}) {
              const title=optionSources[itemKey].dropDownPlaceholder;
              const icon=optionSources[itemKey].icon;
              return(
                  <div className="d-flex justify-content-start align-items-center mt-2 selection-item">
                      <div className="option-label">
                      <b><span role="img" aria-label="label-icon">{icon}</span>{` ${title}`}</b>
                      </div>
                      <div className="dropdown ml-4">
                      <button className="btn btn-primary dropdown-toggle" type="button" id="dropdownMenuButton" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
                          {title}
                      </button>
                      <div className="dropdown-menu" aria-labelledby="dropdownMenuButton">
                          {renderOptionsDropdown()}
                      </div>
                      </div>
                  </div>
              )
          }
          
          export default OptionSelection;
          

          在上面的組件中,我們itemKey從第3行的父組件中獲得prop(param)值。我們假設(shè)itemKey從父組件接收的是CONSOLE。在第4行和第5行,我們首先提取顯示在下拉菜單左側(cè)的標(biāo)題和圖標(biāo)。然后,根據(jù)Boostrap文檔在創(chuàng)建下拉列表時(shí),在第6行的返回函數(shù)中使用HTML標(biāo)簽。

          接下來(lái),我們需要實(shí)現(xiàn)renderOptionsDrop在返回函數(shù)中定義的函數(shù),如下所示:

          import optionSources from  '../optionsSources.json';
          function OptionSelection({itemKey, setOptionInObject}) {
          ...
          const renderOptionsDropdown=()=> {
                  const selectionOptions=optionSources[itemKey].options;
                  return selectionOptions.map((selectionOption, index)=>{
                      return (
                          <div className="dropdown-item pointer" 
                               key={`${index}${selectionOption}`} 
                               onClick={()=> handleDropDownSelection(selectionOption)}
                          >
                              {selectionOption}
                          </div>
                      );
                  })
          }
          ...
          }
          

          在第5行,我們從optionSources JSON對(duì)象獲取特定項(xiàng)的options數(shù)組,并將其存儲(chǔ)在selectionOptions變量中。

          然后在第6行,我們使用map函數(shù)迭代數(shù)組并顯示下拉選擇項(xiàng)。我們必須在第10行使用onClick函數(shù)更新下拉項(xiàng)的選定值。

          然后實(shí)現(xiàn)onClick處理程序中的函數(shù)handleDropDownSelection,如下所示:

          import React,{ useState } from 'react';
          ...
          function OptionSelection({itemKey, setOptionInObject}) {
              const [currentSelectedOption, setSelectedOption]=useState(null);
              const handleDropDownSelection=(consoleOption)=> {
                  setSelectedOption(consoleOption)
                  setOptionInObject(itemKey, consoleOption)
              }
              ...
          }
          

          我們?cè)诘?行輸入了useState hook。它是一個(gè)內(nèi)部函數(shù),允許我們使用狀態(tài)變量的概念動(dòng)態(tài)更新值。關(guān)于這個(gè)函數(shù)的更多信息可以在這里找到:

          • React useState docs:https://reactjs.org/docs/hooks-state.html

          在第7行,我們更新下拉列表的選定選項(xiàng)。在第8行中,我們將選擇的值傳遞回父函數(shù)以進(jìn)行進(jìn)一步處理。

          這個(gè)組件的完整代碼可以在這里找到:https://github.com/codeclassifiers/video-salesprediction-frontend/blob/master/src/components/ConsoleSelection.js

          然后我們?cè)趕rc文件夾中導(dǎo)入此選項(xiàng)并對(duì)服務(wù)器進(jìn)行API調(diào)用。完整的代碼可以在這里找到:

          • https://github.com/codeclassifiers/video-salesprediction-frontend/blob/master/src/App.js

          然后在handleInputSubmission函數(shù)中對(duì)后端進(jìn)行API調(diào)用,如下所示:

          import React, {useState} from 'react';
          import axios from 'axios';
          function App() {
            ...
            const handleInputSubmission=()=> {
              if(selectedObject && Object.keys(selectedObject).length===7) {
                ...
                axios.post(process.env.REACT_APP_HEROKU_SERVER_URL, selectedObject)
                .then(function (response) {
                  setPredictionLoading(false)
                  setModelPrediction(response.data.result)
                })
                .catch(function (error) {
                  setPredictionLoading(false)
                  setRequestFailed("Some error ocurred while fetching prediction")
                });
              } else {
                setRequestFailed("Please select all fields before submitting request")
              }
            }
          }
          

          我們正在使用Axios npm模塊對(duì)后端Heroku服務(wù)器進(jìn)行POST API調(diào)用。確保在process.env.REACT_APP_HEROKU_SERVER_URL占位符的第8行上添加自己的Heroku服務(wù)器URL,以接收來(lái)自服務(wù)器API的響應(yīng)。

          最好將API URL變量保存在.env文件中,然后在部署環(huán)境中進(jìn)行設(shè)置。可以在這里找到更多詳細(xì)信息:

          • Env變量: https://create-react-app.dev/docs/adding-custom-environment-variables/

          在此處找到Github上的前端應(yīng)用程序的完整資源:

          • 視頻游戲銷(xiāo)售預(yù)測(cè)前端:https://github.com/codeclassifiers/video-salesprediction-frontend

          這使我們有了在線(xiàn)部署Web應(yīng)用程序的最后一步。因此,請(qǐng)耐心一些,讓我們開(kāi)始項(xiàng)目的最后一步。

          第5部分:將客戶(hù)端應(yīng)用程序部署到Netlify

          Netlify是一個(gè)可以輕松在線(xiàn)部署靜態(tài)網(wǎng)站的平臺(tái)。在部署使用createreact app模塊生成的應(yīng)用程序時(shí),它有一個(gè)非常簡(jiǎn)單的過(guò)程。我們將利用此服務(wù)在線(xiàn)托管我們的web應(yīng)用程序。

          首先,我們需要在Github上創(chuàng)建一個(gè)帳戶(hù)。

          然后,我們需要將前端文件夾上傳到Github存儲(chǔ)庫(kù)。我們可以按照官方文檔中顯示的步驟將項(xiàng)目部署到Github:官方文檔(https://docs.github.com/en/github/importing-your-projects-to-github/adding-an-existing-project-to-github-using-the-command-line)

          一旦該項(xiàng)目在GitHub上進(jìn)行部署,通過(guò)遵循以下官方文檔即可簡(jiǎn)單,直接地完成netlify的過(guò)程:Netlify Deploy(https://www.netlify.com/blog/2016/09/29/a-step-by-step-guide-deploying-on-netlify/)

          如果你已在上一步中將環(huán)境變量用于服務(wù)器URL,請(qǐng)確保如本文檔所示將其添加到netlify dashboard中。

          最后,我們將提供一個(gè)如下所示的網(wǎng)絡(luò)應(yīng)用程序:

          額外提示

          • 改進(jìn)UI和配色方案

          老實(shí)說(shuō),上面的UI非常簡(jiǎn)單。它沒(méi)有很好的配色方案(主要是因?yàn)橄裎疫@樣的開(kāi)發(fā)人員不是優(yōu)秀的設(shè)計(jì)師)。你可以改善設(shè)計(jì)并調(diào)整CSS,以更好地查看網(wǎng)頁(yè)。

          尾注

          這樣就完成了從機(jī)器學(xué)習(xí)hackathon數(shù)據(jù)集創(chuàng)建銷(xiāo)售預(yù)測(cè)Web應(yīng)用程序的過(guò)程。


          主站蜘蛛池模板: 亚洲一区二区在线视频| 中文字幕精品一区二区日本| 午夜一区二区免费视频| 无码毛片一区二区三区中文字幕| 区三区激情福利综合中文字幕在线一区亚洲视频1 | 国产精品无码一区二区三区免费| 日本人真淫视频一区二区三区| 日韩亚洲AV无码一区二区不卡| 男人的天堂亚洲一区二区三区| 国产91大片精品一区在线观看| 国产福利电影一区二区三区,日韩伦理电影在线福| 成人午夜视频精品一区| 日韩在线视频不卡一区二区三区 | 亚洲AV午夜福利精品一区二区 | 暖暖免费高清日本一区二区三区| 国产一区二区在线看| 国产综合一区二区在线观看| 精品免费AV一区二区三区| 日本一区免费电影| 国产精品日韩欧美一区二区三区 | 亚洲制服中文字幕第一区| 中文字幕aⅴ人妻一区二区| 一区二区三区美女视频| 一区二区三区免费视频观看| 无码人妻一区二区三区在线水卜樱| 国模无码一区二区三区| 国产麻豆精品一区二区三区| AV无码精品一区二区三区| 国产一区二区三区乱码网站| 亚洲综合激情五月色一区| 无码日韩精品一区二区免费| 精品免费国产一区二区三区| 免费萌白酱国产一区二区| 一区二区三区在线播放视频| 国产情侣一区二区三区| 国产精品视频第一区二区三区| 亚洲AV乱码一区二区三区林ゆな| 91video国产一区| 成人国产精品一区二区网站公司| 国产乱人伦精品一区二区在线观看| 夜夜精品视频一区二区|