2019年7月3日 星期三

php .htaccess的一些筆記

想控制瀏覽器cache可以透過.htaccess來下手,如下所示。
<IfModule mod_headers.c>
#no cache for html
<FilesMatch "\.(php)$">
Header set Cache-Control "max-age=0, no-cache, no-store, must-revalidate"
Header set Pragma "no-cache"
</FilesMatch>
# a year for image files
<FilesMatch "\.(ico|jpg|jpeg|png|gif|swf)$">
Header set Cache-Control "max-age=31536000, public"
</FilesMatch>
# One month for css and js
<FilesMatch "\.(js|css)$">
Header set Cache-Control "max-age=2628000, public"
</FilesMatch>
</IfModule>
FilesMatch的設定是看副檔名設定,原以為是看瀏覽器的檔案類型,譬如php會顯示為html的類型,結果不是,在這邊紀錄一下,免得下次又忘記。

2018年3月29日 星期四

Google Map How To Get An API KEY

好久沒寫文章了,在國外生活了一年,一眨眼我又在台灣了,這次是為了碩士論文需要使用到Google Map,腦袋好久沒「認真」的運動了,努力!

要使用Google Map API,要先有金鑰,我們可以至Google Map API取得金鑰,為什麼要在這邊取得金鑰,因為「它將自動引導您完成啟用 Google Maps JavaScript API 與所有相關服務的程序。」,超方便的,它會一步步引導你取得自己的金鑰,一下子的功夫金鑰到手。

2017年8月28日 星期一

Cannot start session without errors in phpMyAdmin

Cannot start session without errors, please check errors given in your PHP and/or webserver log file and configure your PHP installation properly.

session.save_path = 'C:\Windows\Temp'
solve!

2017年1月23日 星期一

C# MVC View 底下字串(string)的顯示方式

教學文章:http://demo.tc/post/679

因為需要印出html碼,所以搜尋了一下方法,發現有幾個常用的函數,以下都是我目前用過的,很少…之後有再使用到相關的東西會繼續補上

  1. Html.Encode(string):會被編碼過後印出(在我認知就是被當成文字),像如果寫了<span>test</span>,<span>的部份不會被當成html標籤,會被當成文字直接印在頁面上。
  2. Html.Raw(string):不會經過編碼,如果寫了<span>test</span>,<span>會變成html的標籤,頁面上只會看到test。
    @string test = "<span>test</span>";
    @Html.Raw(new_head)
  3. HtmlString:一開始就指定此字串為html字元,作用同Html.Raw,不過要印出時只要直接@字串名稱就好。
    @HtmlString test = new HtmlString("<div class=\"clearfix\"> </div></div>");
    @test


2017年1月20日 星期五

C# MVC jQuery FileUpload

範例套件下載:https://github.com/daniel-williams/jQuery-File-Upload.MVC4
參考畫面(Basic Plus UI):http://blueimp.github.io/jQuery-File-Upload/

jQuery.FileUpload有分很多版本,我使用的是Basic Plus UI這一個版本。雖然NuGet套件裡也有jQuery.FileUpload,不過跟範例的版本有落差,建議大家可以先用套件的檔案,再來做調整。

至於我修改了哪些地方呢?
  1. 範例是抓檔案列出清單,而我的專案需要配合DB,所以改成由DB中撈出資料後再去抓上傳的圖檔。
  2. 原來的檔案都是上傳到同一個地方,在這邊我把它們分門別類,依照模組(modelName)的不同上傳到不同的資料夾。
我們需要copy的檔案有:
  • IDGen.cs
  • ImageHandler.cs
  • MimeTypes.cs
  • ViewDataUploadFilesResult.cs
  • styles/FileUpload/全部  (在專案中要改放到Content/底下)
  • scripts/FileUpload/全部  (在專案中的Scripts開頭是大寫)
然後修改前4個檔案的namespace,改成自己的專案名稱,這樣等等就不需要多做using的動作了。

我們先從需要修改的檔案開始改起,首先是App_Start/RouteConfig.cs,增加綠色的部份
    public static void RegisterRoutes(RouteCollection routes)
    {
      routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

      // configure HttpHandler for serving static images
      HttpMethodConstraint GetFileMethodConstraints = new HttpMethodConstraint(new string[] { "GET" });
      Route GetFileRoute = new Route(
          url: "Files/{id}/{filename}",
          routeHandler: new DobImageRouteHandler(),
          defaults: null,
          constraints: new RouteValueDictionary { { "httpMethod", GetFileMethodConstraints } }
      );
      routes.Add("GetFileRoute", GetFileRoute);


      //routes.MapRoute(
      //    name: "Get File",
      //    url: "Files/{id}/{filename}",
      //    defaults: new { controller = "Files", action = "Find" },
      //    constraints: new { httpMethod = new HttpMethodConstraint("GET") }
      //);

      routes.MapRoute(
          name: "Delete File",
          url: "Files/{id}",
          defaults: new { controller = "Files", action = "Delete" },
          constraints: new { httpMethod = new HttpMethodConstraint("DELETE") }
      );

      routes.MapRoute(
          name: "Get Json File List",
          url: "Files/{id}",
          defaults: new { controller = "Files", action = "List" },
          constraints: new { httpMethod = new HttpMethodConstraint("GET") }
      );

      routes.MapRoute(
          name: "Post Files",
          url: "Files/{id}",
          defaults: new { controller = "Files", action = "Uploads" },
          constraints: new { httpMethod = new HttpMethodConstraint("POST") }
      );

      routes.MapRoute(
          name: "Default",
          url: "{controller}/{action}/{id}",
          defaults: new { controller = "Home", action = "Login", id = UrlParameter.Optional }
      );

    }

再編輯BundleConfig.cs,加入綠色的部份
    public static void RegisterBundles(BundleCollection bundles)
    {
      ...(原本的內容)

      bundles.Add(new ScriptBundle("~/bundles/jqueryfileupload").Include(
                "~/Scripts/FileUpload/vendor/jquery.ui.widget.js",
                "~/Scripts/FileUpload/tmpl.js",
                "~/Scripts/FileUpload/load-image.js",
                "~/Scripts/FileUpload/canvas-to-blob.js",
                "~/Scripts/FileUpload/jquery.iframe-transport.js",
                "~/Scripts/FileUpload/jquery.fileupload.js",
                "~/Scripts/FileUpload/jquery.fileupload-fp.js",
                "~/Scripts/FileUpload/jquery.fileupload-ui.js"));
                //"~/Scripts/FileUpload/main.js"));

      bundles.Add(new StyleBundle("~/Content/jqueryfileupload").Include(
                  "~/Content/FileUpload/jquery.fileupload-ui.css"));

    }

打開你要加入此功能的頁面,加入以下內容,因為我沒有用範例的bootstrap套件,所以css的部份有些許不同,不然按鈕上的圖示會無法顯示。原本main.js的內容則是移到頁面中,因為要加上modelName的參數
<!-- 
加入前先檢查一下Views/Shared/_Layout.cshtml是不是有下列兩個參數了,否則會出錯
@RenderSection("scripts", required: false)
@RenderSection("styles", required: false)
//-->

@section scripts {
  @Scripts.Render("~/bundles/jqueryfileupload")
}

@section styles
{
  @Styles.Render("~/Content/jqueryfileupload")
}

<form id="fileupload" action="@Url.Action("Uploads","Files")" method="POST" enctype="multipart/form-data">
    @Html.Hidden("modelName", "Product")
    <!-- The fileupload-buttonbar contains buttons to add/delete files and start/cancel the upload -->
    <div class="row fileupload-buttonbar">
      <div class="col-md-7">
        <!-- The fileinput-button span is used to style the file input field as button -->
        <span class="btn btn-success fileinput-button">
          <i class="glyphicon glyphicon-plus"></i>
          <span>增加檔案...</span>
          <input type="file" name="files[]" multiple>
        </span>
        <button type="submit" class="btn btn-primary start">
          <i class="glyphicon glyphicon-upload"></i>
          <span>開始上傳</span>
        </button>
        <button type="reset" class="btn btn-warning cancel">
          <i class="glyphicon glyphicon-ban-circle"></i>
          <span>取消上傳</span>
        </button>
        <button type="button" class="btn btn-danger delete">
          <i class="glyphicon glyphicon-trash"></i>
          <span>刪除</span>
        </button>
        <input type="checkbox" class="toggle">
        <!-- The global file processing state -->
        <span class="fileupload-process"></span>
      </div>
      <!-- The global progress state -->
      <div class="col-md-3 fileupload-progress fade">
        <!-- The global progress bar -->
        <div class="progress progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100">
          <div class="progress-bar progress-bar-success" style="width:0%;"></div>
        </div>
        <!-- The extended global progress state -->
        <div class="progress-extended">&nbsp;</div>
      </div>
      <!-- go back pritave page -->
      <div class="col-md-2 text-right">
        <input type="button" value="返回" onclick="comeback();" class="btn btn-default" />
      </div>
    </div>
<!-- The table listing the files available for upload/download -->
<table role="presentation" class="table table-striped"><tbody class="files"></tbody></table>
  </form>
</div>

<!-- The template to display files available for upload -->
<script id="template-upload" type="text/x-tmpl">
  {% for (var i=0, file; file=o.files[i]; i++) { %}
  <tr class="template-upload fade">
    <td class="preview"><span class="fade"></span></td>
    <td class="name"><span>{%=file.name%}</span></td>
    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
    {% if (file.error) { %}
    <td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
    {% } else if (o.files.valid && !i) { %}
    <td>
      <div class="progress progress-success progress-striped active" role="progressbar" aria-valuemin="0" aria-valuemax="100" aria-valuenow="0"><div class="bar" style="width:0%;"></div></div>
    </td>
    <td class="start">
      {% if (!o.options.autoUpload) { %}
      <button class="btn btn-primary">
        <i class="glyphicon glyphicon-upload"></i>
        <span>開始上傳</span>
      </button>
      {% } %}
    </td>
    {% } else { %}
    <td colspan="2"></td>
    {% } %}
    <td class="cancel">
      {% if (!i) { %}
      <button class="btn btn-warning">
        <i class="glyphicon glyphicon-ban-circle"></i>
        <span>取消上傳</span>
      </button>
      {% } %}
    </td>
  </tr>
  {% } %}
</script>


<!-- The template to display files available for download -->
<script id="template-download" type="text/x-tmpl">
  {% for (var i=0, file; file=o.files[i]; i++) { %}
  <tr class="template-download fade">
    {% if (file.error) { %}
    <td></td>
    <td class="name"><span>{%=file.name%}</span></td>
    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
    <td class="error" colspan="2"><span class="label label-important">Error</span> {%=file.error%}</td>
    {% } else { %}
    <td class="preview">
      <a href="{%=file.url%}" title="{%=file.name%}" data-gallery="gallery" download="{%=file.name%}"><img src="{%=file.thumbnail_url%}" class="thum" /></a>
    </td>
    <td class="name">
      <a href="{%=file.url%}" title="{%=file.name%}" data-gallery="{%=file.thumbnail_url&&'gallery'%}" download="{%=file.name%}">{%=file.name%}</a>
    </td>
    <td class="size"><span>{%=o.formatFileSize(file.size)%}</span></td>
    <td colspan="2"></td>
    {% } %}
    <td class="delete">
      <button class="btn btn-danger" data-type="{%=file.delete_type%}" data-url="{%=file.delete_url%}" {% if (file.delete_with_credentials) { %} data-xhr-fields='{"withCredentials":true}' {% } %}>
        <i class="glyphicon glyphicon-trash"></i>
        <span>刪除</span>
      </button>
      <input type="checkbox" name="delete" value="1">
    </td>
  </tr>
  {% } %}
</script>

<script type="text/javascript">
  function comeback() {
    window.location.href = "@Url.Action("Index", "Product")";
  }

  $(document).ready(function () {
    'use strict';

    // Initialize the jQuery File Upload widget:
    $('#fileupload').fileupload({
      // Uncomment the following to send cross-domain cookies:
      //xhrFields: {withCredentials: true},

      url: '/Files/@Model?modelName=Product',
      acceptFileTypes: /(\.|\/)(gif|jpe?g|png)$/i,
      maxFileSize: 3000000,
    });

    // Enable iframe cross-domain access via redirect option:
    $('#fileupload').fileupload(
        'option',
        'redirect',
        window.location.href.replace(
            /\/[^\/]*$/,
            '/cors/result.html?%s'
        )
    );

    // Load existing files:
    $.ajax({
      // Uncomment the following to send cross-domain cookies:
      //xhrFields: {withCredentials: true},
      url: $('#fileupload').fileupload('option', 'url'),
      dataType: 'json',
      context: $('#fileupload')[0]
    }).done(function (result) {
      $(this).fileupload('option', 'done')
          .call(this, null, { result: result });
    });

  });
</script>

增加一個Controller檔案,命名為FilesController.cs,copy範例裡的同名檔案貼上內容,然後把所缺的東西一一 using 進來。至於db的部份,則看個人是怎麼寫的自行加上,我有寫成一個class,所以是直接引入我寫的class
      protected connDB D = new connDB();
        //
        // GET: /Files/
      private string _Dir = "D:/project";
      private string _StorageRoot;
      private string StorageRoot
      {
        get { return _StorageRoot; }
      }

      public FilesController()
      {
        //string modelName = "Product";
        _StorageRoot = Path.Combine(_Dir + "/Files/");
      }

      [HttpGet]
      public ActionResult List(int id, string modelName)
      {
        D.Open();
        DataTable Table = D.GetTable("SELECT * FROM product_img WHERE product_id = '" + D.ToString(id) + "'");
        D.Close();

        var fileData = new List<ViewDataUploadFilesResult>();

        DirectoryInfo dir = new DirectoryInfo(StorageRoot + modelName);

        if (dir.Exists)
        {
          foreach (DataRow row in Table.Rows)
          {
            var fileNameEncoded = HttpUtility.HtmlEncode(row["filename"]);
            var relativePath = "/Files/" + modelName + "/" + fileNameEncoded;

            fileData.Add(new ViewDataUploadFilesResult()
            {
              url = relativePath,
              thumbnail_url = relativePath, //@"data:image/png;base64," + EncodeFile(fullName),
              name = fileNameEncoded,
              type = D.ToString(row["filetype"]),
              size = D.ToInt(row["filesize"]),
              delete_url = "/Files/" + id + "?filename=" + fileNameEncoded + "&modelName=" + modelName,
              delete_type = "DELETE"
            });
          }

          /*
          string[] extensions = MimeTypes.ImageMimeTypes.Keys.ToArray();

          FileInfo[] files = dir.EnumerateFiles()
                   .Where(f => extensions.Contains(f.Extension.ToLower()))
                   .ToArray();

          if (files.Length > 0)
          {
            foreach (FileInfo file in files)
            {
              var fileId = file.Name.Substring(0, 20);
              var fileNameEncoded = HttpUtility.HtmlEncode(file.Name.Substring(21));
              var relativePath = "/App_Data/" + modelName + "/" + fileNameEncoded;

              fileData.Add(new ViewDataUploadFilesResult()
              {
                url = relativePath,
                thumbnail_url = relativePath, //@"data:image/png;base64," + EncodeFile(fullName),
                name = fileNameEncoded,
                type = MimeTypes.ImageMimeTypes[file.Extension],
                size = Convert.ToInt32(file.Length),
                delete_url = relativePath,
                delete_type = "DELETE"
              });
            }
          }
          */
        }

        var serializer = new JavaScriptSerializer();
        serializer.MaxJsonLength = Int32.MaxValue;

        var result = new ContentResult
        {
          Content = "{\"files\":" + serializer.Serialize(fileData) + "}",
        };
        return result;
      }

      public ActionResult Find(string id, string filename, string modelName)
      {
        if (id == null || filename == null)
        {
          return HttpNotFound();
        }

        //var filePath = Path.Combine(_StorageRoot, id + "-" + filename);
        var filePath = Path.Combine("~/Files/" + modelName, filename); 

        FileStreamResult result = new FileStreamResult(new System.IO.FileStream(filePath, System.IO.FileMode.Open), GetMimeType(filePath));
        result.FileDownloadName = filename;

        return result;
      }

      [HttpPost]
      [AcceptVerbs(HttpVerbs.Post)]
      public ActionResult Uploads(int id, string modelName)
      {
        var fileData = new List<ViewDataUploadFilesResult>();
        D.Open();
        int maxid = D.ToInt(D.GetValue("SELECT MAX(orderid) FROM product_img WHERE product_id = '" + D.ToString(id) + "'"));
        maxid = (maxid < 1) ? 0 : maxid;
        
        foreach (string file in Request.Files)
        {
          UploadWholeFile(modelName, id, maxid, Request, fileData);
        }

        D.Close();

        var serializer = new JavaScriptSerializer();
        serializer.MaxJsonLength = Int32.MaxValue;

        var result = new ContentResult
        {
          Content = "{\"files\":" + serializer.Serialize(fileData) + "}",
        };
        return result;
      }

      private void UploadWholeFile(string modelName, int id, int maxid, HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
      {

        D.Open();
        for (int i = 0; i < request.Files.Count; i++)
        {

          HttpPostedFileBase file = request.Files[i];

          ++maxid;
          //upload files
          string tempName = IDGen.NewID() + "_";

          if (file.ContentLength == 0)
            continue;

          var fileName = tempName + Path.GetFileName(file.FileName);
          var fileNameEncoded = HttpUtility.HtmlEncode(fileName);
          var fullPath = Path.Combine(Server.MapPath("~/Files/" + modelName), fileName);

          file.SaveAs(fullPath);

          //save to db
          Dictionary<string, string> data = new Dictionary<string, string>();
          data["product_id"] = D.ToString(id);
          data["filename"] = D.ToString(fileNameEncoded);
          data["filesize"] = D.ToString(file.ContentLength);
          data["orderid"] = D.ToString(maxid);
          D.InsertDictionary("product_img", data);

          statuses.Add(new ViewDataUploadFilesResult()
          {
            url = "/Files/" + modelName + "/" + fileNameEncoded,
            thumbnail_url = "/Files/" + modelName + "/" + fileNameEncoded, //@"data:image/png;base64," + EncodeFile(fullName),
            name = fileNameEncoded,
            type = file.ContentType,
            size = file.ContentLength,
            delete_url = "/Files/" + id + "?filename=" + fileNameEncoded + "&modelName=" + modelName,
            delete_type = "DELETE"
          });
        }
      }

      /*
      private void UploadWholeFile(HttpRequestBase request, List<ViewDataUploadFilesResult> statuses)
      {
        for (int i = 0; i < request.Files.Count; i++)
        {
          HttpPostedFileBase file = request.Files[i];

          var fileId = IDGen.NewID();
          var fileName = Path.GetFileName(file.FileName);
          var fileNameEncoded = HttpUtility.HtmlEncode(fileName);
          var fullPath = Path.Combine(StorageRoot, fileId + "-" + fileName);

          file.SaveAs(fullPath);

          statuses.Add(new ViewDataUploadFilesResult()
          {
            url = "/Files/" + fileId + "/" + fileNameEncoded,
            thumbnail_url = "/Files/" + fileId + "/" + fileNameEncoded, //@"data:image/png;base64," + EncodeFile(fullName),
            name = fileNameEncoded,
            type = file.ContentType,
            size = file.ContentLength,
            delete_url = "/Files/" + fileId + "/" + fileNameEncoded,
            delete_type = "DELETE"
          });
        }
      }
      */
        
      [AcceptVerbs(HttpVerbs.Delete)]
      public ActionResult Delete(string id, string filename, string modelName)
      {
        if (id == null || filename == null || modelName == null)
        {
          return HttpNotFound();
        }

        var filePath = Path.Combine(_StorageRoot + modelName, filename);

        if (System.IO.File.Exists(filePath))
        {
          System.IO.File.Delete(filePath);
          D.Open();
          D.DataDelete("DELETE FROM product_img WHERE filename = '" + filename + "'");
          D.Close();
        }

        return RedirectToAction("FormImg", "Product");
      }

      private string EncodeFile(string fileName)
      {
        return Convert.ToBase64String(System.IO.File.ReadAllBytes(fileName));
      }

      private string GetMimeType(string filePath)
      {
        return GetMimeType(new FileInfo(filePath));
      }
      private string GetMimeType(FileInfo file)
      {
        return MimeTypes.ImageMimeTypes[file.Extension];
      }

在根目錄下新增一個Files的資料夾,全部都佈屬好後就啟動唄!

畫面會長的像這樣,當然如果你還沒上傳過東西,列表會是空的


希望大家都能順利coding。

2016年12月27日 星期二

C# MVC Pass multiple list from models & view & controller

參考來源:http://stackoverflow.com/questions/36892770/pass-multiple-list-from-multiple-models-to-view-from-controller

Models內容
public class MyViewModel {
   public List<table1> TheFirstTable {get;set;}
   public List<table2> TheSecondTable {get;set;}
}

Controller內容
MyViewModel viewModel = new MyViewModel();
viewModel.TheFirstTable = //first table content
viewModel.TheSecondTable = //second table content
return View(viewmodel);

View內容
//多筆資料
@foreach(var row in Model.TheFirstTable) {
    //do something with the row
}

//單筆資料
@Model.TheSecondTable

這樣就能同時輸出多筆不同的 List 嘍!