讓Content-Disposition解決非英文檔案,下載亂碼問題

這是老問題了,很多老外都忽略這個,我在協助teedy翻譯時,發現下載中文檔案中文會出現空白,看一下程式原來是忘了進行編碼導致,就順手提交一下解法。

#以下為java虛擬碼
CONTENT_DISPOSITION, "inline; filename*=utf-8''" + filenameEncode( "檔名" )

private String filenameEncode(String name) {
try {
return java.net.URLEncoder.encode(name, "UTF-8").replace("+", "%20");
} catch (java.io.UnsupportedEncodingException e) {
e.printStackTrace();
return name;
}
}

參考 :

老派java+eclipse+sprint boot遇到3DES加密問題

API,傳資料只用簡單的3DES加解密,但是java 預設只有3DES的PKCS5Padding,要達到PKCS7Padding(windows c# 只支援PKCS7Padding),就只能請Bouncy Castle幫忙,我使用bcprov-jdk15to18-1.78.1.jar、jdk17。

程式改寫大概如下:

// java 只有padding5,用bouncycastle,改寫成可使用padding7
static {
if(Security.getProvider("BC")!=null) Security.removeProvider("BC");
Security.addProvider(new BouncyCastleProvider());
}


// 使用CBC、PKCS7Padding
原本寫法
Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS5Padding");
改成
Cipher decryptCipher = Cipher.getInstance("DESede/CBC/PKCS7Padding","BC");

在Eclipse開發時很正常,正式執行時,出現以下錯誤:

jce cannot authenticate the provider bc

我查了一下原來jdk 17是17.0.9版,這問題於jdk 17.0.10(2024年1月)解決,就這麼剛好只差一版;最後,我升級至目前最新 jdk17.0.12版就解決了。參考來源


另外 windows uwp c#寫法也幾乎一至,因java是API,windows app為client,所以也就順手了解一下

public static String Encrypt3DesCbcPKCS7Padding(String sIV,String sSecretKey, String sData)
{
TripleDES tdes = TripleDES.Create();
IvParameterSpec ivSpec = new IvParameterSpec(System.Text.Encoding.UTF8.GetBytes(sIV));
SecretKeySpec secretKeySpec = new SecretKeySpec(System.Text.Encoding.UTF8.GetBytes(sSecretKey), "DESede");
Cipher encryptCipher = Cipher.GetInstance("DESede/CBC/PKCS7Padding", "BC");
encryptCipher.Init(Cipher.ENCRYPT_MODE, secretKeySpec, ivSpec);
return Base64.ToBase64String(encryptCipher.DoFinal(System.Text.Encoding.UTF8.GetBytes(sData)));

}

Android取得目前wifi連線資料

API 31(含)以後,寫法變了,

https://developer.android.com/reference/kotlin/android/net/wifi/WifiManager#getConnectionInfo()
照著官方這樣寫,似乎還是無法取得連線資料,須加上 FLAG_INCLUDE_LOCATION_INFO 才行。

WifiManager wifiManager = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE);

final NetworkRequest request = new NetworkRequest.Builder()
.addTransportType(NetworkCapabilities.TRANSPORT_WIFI).build();
ConnectivityManager connectivityManager = activity.getSystemService(ConnectivityManager.class);

ConnectivityManager.NetworkCallback networkCallback = new ConnectivityManager.NetworkCallback(FLAG_INCLUDE_LOCATION_INFO) {
@Override
public void onAvailable(Network network) {}

@Override
public void onCapabilitiesChanged(Network network, NetworkCapabilities networkCapabilities) {
WifiInfo wifiInfo = (WifiInfo) networkCapabilities.getTransportInfo();
String aConnectedWifiSSD = wifiInfo.getSSID().toLowerCase();
Log.d(TAG,"wifiInfo="+wifiInfo);
connectivityManager.unregisterNetworkCallback( this);

}
};
if( wifiManager.isWifiEnabled() ) {
connectivityManager.requestNetwork(request, networkCallback); // For request
connectivityManager.registerNetworkCallback(request, networkCallback); // For listen
}
# 需要的權限
android.Manifest.permission#ACCESS_FINE_LOCATION
android.Manifest.permission#ACCESS_WIFI_STATE

Android開發關於AlertDialog兩三事

開發android都知道,要顯示視窗訊息,不是那直覺方便的,需要搭配Handler參與才能走得順。

# 原本以為myAlterDialog.show()後,就可以馬上執行其他程式商業邏輯,完成後停掉myAlterDialog即可
# (例如我們要實現APP「顯示"請稍後",然後背景執行其他程式商業邏輯,執行完取消"請稍後"」)
# 但我沒想到程式商業邏輯需要寫在Handler裡面才不會讓卡住。

public class MainActivity extends AppCompatActivity {
  AlertDialog.Builder waitBuilder = null;
  AlertDialog waitDialog;
  ...
  protected void onCreate(Bundle savedInstanceState) {
     ...
     waitBuilder = new AlertDialog.Builder(activity);
     waitBuilder.setCancelable(false);
     waitBuilder.setView(R.layout.loading_dialog);
     myAlterDialog = waitBuilder.create();
  }

  public void onStart() {

    adapter.setOnItemLongClickListener(.....){
      ...
      runOnUiThread(new Runnable() {
        @Override
	public void run() {
          myAlterDialog.show();
	  new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
	    @Override
 	    public void run() {
              runOnUiThread(new Runnable() {
                @Override
                public void run() {
                  ...
                  Toast.makeText(activity,"success"....
                  myAlterDialog.dismiss();
                }
              });
	    }
	  }, 300);	
        }
      }
      ...
     });		
   });
}

麻瓜勿擾,GIT文字筆記

2011年第一隻android app有數萬下載量,到2019年我又練習寫了一個「GIT文字筆記」,是個「麻瓜勿擾的APP」。
該APP有開源,久了我也幾乎忘了有這,最近收到app要改目標版本,否則要下架,於是我更新時,再加碼限制android 13或以上才能安裝使用;
果然幾乎沒人用,到目前只有50+下載 XD

我這APP也很奇葩,用git shallow減少clone下載版本量呢。

https://play.google.com/store/apps/details?id=inmethod.gitnotetaking

java jdbc 連到 M$ access “ucanaccess”

年紀一大把了,還在搞Acess,慘

你可以存取的(u can access),到此下載

解開後,除了本體ucanaccess-xxx.jar外,在lib目錄下的jar檔也需要。

其中存取access的關鍵jackcess-xxx.jar (目前最新版4.0.6),可另外下載取代ucanaccess內建的3.0.1版本

JDBC Connection String

jdbc:ucanaccess://c:/xxx.mdb;memory=false

「FydeOS – 为中国用户打造的ChromeOS」初體驗

公司希望Android撰寫的測試程式移植到windows平台上,但是這樣做很耗時,程式要重新開發; 因此我想到讓筆電或電腦桌機能夠直接執行android, 這樣就能解決問題了。

我排除已知的解決方案之一-Android模擬器、windows 11模擬andorid不考慮(因為要停用了)。

最後我希望類似chrome book 使用chrome OS直接支援android 的方式,沒想到還真的找到了 –FydeOS

安裝FydeOS有些眉角要注意的:

  1. 筆電-panasonic CF-NX3,12G Ram
  2. 官方安裝方式,我要連續安裝好幾次才成功
  3. 安裝時,我的舊電腦BIOS先調整非UEFI才能安裝,安裝後再調回UEFI,才能啟用開發者模式(v18版)
  4. 安裝時,我使用本機帳號,不線上註冊
  5. 安裝完畢,FydeOS系統成功開機,啟用Android位置權限失敗,後來發現安裝Open GApps,GApps會將Play商店裝起來,才能啟用位置權限
  6. Play商店無法登入(未通過Play防護安全認證)
  7. 其他方面很順,簡直就是chrome book

java Servlet 取得遠端ip

原來不是這麼簡單

private static final String[] VALID_IP_HEADER_CANDIDATES = { 
  "X-Forwarded-For",
  "Proxy-Client-IP",
  "WL-Proxy-Client-IP",
  "HTTP_X_FORWARDED_FOR",
  "HTTP_X_FORWARDED",
  "HTTP_X_CLUSTER_CLIENT_IP",
  "HTTP_CLIENT_IP",
  "HTTP_FORWARDED_FOR",
  "HTTP_FORWARDED",
  "HTTP_VIA",
  "REMOTE_ADDR" 
};

public static String getClientIpAddress(HttpServletRequest request) {
  for (String header : VALID_IP_HEADER_CANDIDATES) {
    String ipAddress = request.getHeader(header);
    if (ipAddress != null && ipAddress.length() != 0 && !"unknown".equalsIgnoreCase(ipAddress)) {
      return ipAddress;
    }
  }
  return request.getRemoteAddr();
}
1 2 3 ... 5