<!doctype html>
<html lang="zh-Hant-TW">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>上傳資料與多檔</title>
</head>
<body>
<div>
<div>備註<input type="text" id="inpNote"></div>
<div>檔案<input type="file" id="inpFiles" multiple></div>
<div><button type="button" onclick="uploadData();">上傳</button></div>
<div id="divResult"></div>
</div>
<script>
async function uploadData()
{
let oBtn=event.target;
oBtn.setAttribute("disabled", "true");
while(true)
{
let strNote=document.getElementById("inpNote").value;
let oFiles=document.getElementById("inpFiles");
let oFormData=new FormData();
oFormData.append("strJson", JSON.stringify({ "strNote": strNote }));
for(let i=0; i<oFiles.files.length; i++)
{
oFormData.append("oaFile[]", oFiles.files[i]);
}
try
{
var oResponse=await fetch("upload.php", { method: "POST", body: oFormData });
var strJson=await oResponse.text();
var oJson=JSON.parse(strJson);
document.getElementById("divResult").textContent=strJson;
}
catch(oError)
{
console.log({ "oResponse":oResponse, "strJson":strJson, "oError":oError });
alert((oResponse.url+"\n"+oResponse.status+"\n"+oResponse.statusText)+"\n\n"+strJson+"\r\n"+(oError.toString()));
break;
}
break;
}
oBtn.removeAttribute("disabled");
}
</script>
</body>
</html>
<?php
header("Content-Type: application/json; charset=utf-8");
// 資料部份
$oJson=json_decode($_POST['strJson']);
// 檔案部份
$iFileCount=count($_FILES['oaFile']['name']);
for($i=0; $i<$iFileCount; $i++)
{
$strSrcFilePath=$_FILES['oaFile']['tmp_name'][$i];
$strSrcFileName=basename($_FILES['oaFile']['name'][$i]);
$strDstFilePath="uploadfiles/" . $strSrcFileName;
move_uploaded_file($strSrcFilePath, $strDstFilePath);
}
// 回傳json
$oOutData=new stdClass();
$oOutData->iFileCount=$iFileCount;
$oOutData->strNote=$oJson->strNote;
echo json_encode($oOutData, JSON_UNESCAPED_UNICODE); // 輸出 json 資料
?>
<?php
// modified:2026-06-04 11:31:00
// $oMySQL=new CMySQL();
// $oR=$oMySQL->insUpdDel("default", "insert into test(i1,vc1) select 1,'馬蓋仙' union all select 140,'老人家'");
// echo '<pre>';
// var_dump($oR);
// echo '</pre>';
// echo '<hr>';
// $oR=$oMySQL->bigQuery("default", "select 'q1',i1,vc1 from test where i1<=39; select 'q2',i1,vc1 from test where i1>39;");
// echo '<pre>';
// var_dump($oR);
// echo '</pre>';
class CMySQLSQLResult_unit
{
public $straFieldName=array();
public $booaTextType=array();
public $iRows=0;
public $iCols=0;
public $aryRawData=array();
public function getVal($iRow, $strFieldName)
{
$iCol=array_search($strFieldName, $this->straFieldName);
if($iCol===false) { return 'field name not found!'; }
if($iRow>=$this->iRows) { return 'row index out of range!'; }
return $this->aryRawData[$iRow][$iCol];
}
}
class CMySQLSQLResult
{
public $booInsUpdDel=false; // 增修刪=true, 大查詢=false
public $booSuccess=false;
public $strErrorMessage='';
// 增修刪↓
public $iAffectedRows=0;
// 大查詢↓
public $iResultAmount=0;
public $aryResult=array(); // 裝載 CMySQLSQLResult_unit 的陣列,用 array_push()
}
class CMySQL
{
public function getAryConnection($strWho)
{
// [customvalue]
if($strWho=='another') { return array('ip1','username1','password1','database1',3306,'utf8mb4'); }
else { return array('127.0.0.1','root','a/6u3u;35/4','dbChildmo',3306,'utf8mb4'); }
}
// 參數正規化,避免 SQL Injection
public function getStrNum($value) { return strval(floatval($value)); }
public function getStrNormalizedString($value) { return str_replace("'","''",$value); }
public function getStrAnsiTxt($value) { return "'" . $this->getStrNormalizedString($value) . "'"; }
public function getStrUniTxt($value) { return "'" . $this->getStrNormalizedString($value) . "'"; }
public function getStrAnsiLikeTxt($value) { return "'" . '%' . $this->getStrNormalizedString($value) . '%' . "'"; }
public function getStrUniLikeTxt($value) { return "'" . '%' . $this->getStrNormalizedString($value) . '%' . "'"; }
public function getStrFreeText($value) { return $value; }
public function insUpdDel($strWho, $strSQL)
{
$oR=new CMySQLSQLResult();
$oR->booInsUpdDel=true;
while(true)
{
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
// 連線到資料庫
list($strIP, $strUID, $strPWD, $strDB, $iPort, $strEncoding)=$this->getAryConnection($strWho);
$oConn=new mysqli($strIP, $strUID, $strPWD, $strDB, $iPort);
if($oConn->connect_errno)
{
$oR->booSuccess=false;
$oR->strErrorMessage='資料庫連線失敗!ErrMsg:' . $oConn->connect_error;
break;
}
while(true)
{
// 設定編碼
if($oConn->set_charset($strEncoding)==false)
{
$oR->booSuccess=false;
$oR->strErrorMessage='設定(' . $strEncoding . ')編碼失敗!';
break;
}
// 執行 SQL 指令劇本
if($oConn->query($strSQL)==false)
{
$oR->booSuccess=false;
$oR->strErrorMessage='資料庫異動失敗!ErrMsg:' . $oConn->error;
break;
}
// 取得資訊
$oR->booSuccess=true;
$oR->iAffectedRows=$oConn->affected_rows;
break;
}
// 釋放連線
$oConn->close();
break;
}
return $oR;
}
public function bigQuery($strWho, $strSQL)
{
$oR=new CMySQLSQLResult();
$oR->booInsUpdDel=false;
while(true)
{
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
// 連線到資料庫
list($strIP, $strUID, $strPWD, $strDB, $iPort, $strEncoding)=$this->getAryConnection($strWho);
$oConn=new mysqli($strIP, $strUID, $strPWD, $strDB, $iPort);
if($oConn->connect_errno)
{
$oR->booSuccess=false;
$oR->strErrorMessage='資料庫連線失敗!ErrMsg:' . $oConn->connect_error;
break;
}
while(true)
{
// 設定編碼
if($oConn->set_charset($strEncoding)==false)
{
$oR->booSuccess=false;
$oR->strErrorMessage='設定(' . $strEncoding . ')編碼失敗!';
break;
}
// 執行 SQL 指令劇本
if($oConn->multi_query($strSQL)==false)
{
$oR->booSuccess=false;
$oR->strErrorMessage='資料庫異動失敗!ErrMsg:' . $oConn->error;
break;
}
// 逐個結果集檢視
$oR->booSuccess=true;
while(true)
{
// 讀取資料集
$oResult=$oConn->store_result();
if($oResult===false)
{
// 例如 set @x=1; 也會傳回查詢成功,但無結果集,僅做個記錄。放在 errormessage ,不破壞之前的結構。
$oR->strErrorMessage=$oR->strErrorMessage . '(' . $oConn->connect_error . ')';
}
else
{
// 新增一個單元
$oR->iResultAmount++;
$oUnit=new CMySQLSQLResult_unit();
$oUnit->iCols=$oResult->field_count;
$oUnit->iRows=$oResult->num_rows;
$oFieldInfo=$oResult->fetch_fields();
for($i=0; $i<$oUnit->iCols; $i++)
{
$oUnit->straFieldName[]=$oFieldInfo[$i]->name;
$oUnit->booaTextType[]=in_array($oFieldInfo[$i]->type, array(MYSQLI_TYPE_VAR_STRING,MYSQLI_TYPE_STRING,MYSQLI_TYPE_CHAR,MYSQLI_TYPE_TINY_BLOB,MYSQLI_TYPE_MEDIUM_BLOB,MYSQLI_TYPE_LONG_BLOB,MYSQLI_TYPE_BLOB));
}
while($oRow=$oResult->fetch_row())
{
$oUnit->aryRawData[]=$oRow;
}
$oR->aryResult[]=$oUnit;
// 釋放資料集資源
$oResult->free_result();
}
// 查看是否還有資料集
if($oConn->more_results()==false) { break; }
// 準備前往下一個資料集
if($oConn->next_result()==false)
{
$oR->booSuccess=false;
$oR->strErrorMessage=$oR->strErrorMessage . '。資料集往下失敗!ErrMsg:' . $oConn->connect_error;
break;
}
}
break;
}
// 釋放連線
$oConn->close();
break;
}
return $oR;
}
}
?>
<!doctype html>
<html lang="zh-Hant-TW">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<title>mp3檔案</title>
<style>
div.classDiv > a
{
display: block;
font-size: 16pt;
margin: 0.3em;
}
a:link, a:visited
{
color: blue;
text-decoration: none;
}
</style>
</head>
<body>
<div class="classDiv">
<?php
$straFile=array_values(array_diff(scandir("."), ['.', '..', 'index.php']));
for($i=0; $i<count($straFile); $i++)
{
echo "<a href='" . $straFile[$i] . "' download='" . $straFile[$i] . "'>" . $straFile[$i] . "</a>";
}
?>
</div>
</body>
</html>
取得訊息範例
2407.565181,N,12041.025767,E,170526,231638.0,44.4,11.6,266.2
#define iModemRx 26
#define iModemTx 27
#define iCharArrayMax 1024
#define iLed 12
#include <ctype.h>
HardwareSerial SerialAT(1);
char caATCmd[]="AT+CGPSINFO";
char caATKeyword[]="+CGPSINFO:";
char caPhp[]="cgpsinfo.php";
int iLedVal=0;
void qa(const char caQuestion[], char caAnswer[])
{
caAnswer[0]='\0';
while(SerialAT.available()) { SerialAT.read(); } // clear leftover data
SerialAT.write(caQuestion);
SerialAT.write("\r\n");
delay(50);
int iLen=0;
unsigned long ulLast=millis();
int iMaxMilliSecond=10000;
// maybe multi lines
while((millis()-ulLast)<iMaxMilliSecond)
{
while(SerialAT.available())
{
char c1=SerialAT.read();
ulLast=millis();
if(iLen<(iCharArrayMax-1))
{
caAnswer[iLen]=c1;
iLen++;
caAnswer[iLen]='\0';
}
}
if(strstr(caAnswer, "\r\nOK\r\n") || strstr(caAnswer, "\r\nERROR\r\n")) { iMaxMilliSecond=300; }
delay(1);
}
}
bool getBooQA(const char caQuestion[], const char caKeyword[])
{
static char caAnswerBuffer[iCharArrayMax];
qa(caQuestion, caAnswerBuffer);
if(strstr(caAnswerBuffer, caKeyword)!=NULL) { return true; }
return false;
}
void setup()
{
static char caAnswer[iCharArrayMax];
pinMode(iLed, OUTPUT);
digitalWrite(iLed, 1);
Serial.begin(115200); // esp32
SerialAT.begin(115200, SERIAL_8N1, iModemRx, iModemTx); // sim7600g-h
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
delay(3000);
while(SerialAT.available()) SerialAT.read();
while(getBooQA("AT", "OK")==false) { delay(1000); } // wait until AT is OK
// enable GPS via AT+CGPS=1
qa("AT+CGPS=0", caAnswer);
delay(3000);
qa("AT+CGPS=1", caAnswer);
// network registration
int iRetry=0;
while(getBooQA("AT+CGDCONT=1,\"IP\",\"internet\"", "\r\nOK\r\n")==false)
{
delay(1000);
iRetry++;
if(iRetry>10) { break; }
}
if(iRetry>10)
{
digitalWrite(4, LOW);
delay(500);
digitalWrite(4, HIGH);
delay(8000);
ESP.restart();
}
digitalWrite(iLed, 0);
}
bool getBooExtractGPSDataOk(char caInput[], char caOutput[], int iOutputMax, const char caKeyword[])
{
int iIdxInput=0;
int iIdxOutput=0;
bool booFound=false;
caOutput[0]='\0';
while(true)
{
if(caInput[iIdxInput]=='\0') { break; }
if(strncmp(&caInput[iIdxInput], caKeyword, strlen(caKeyword))==0)
{
booFound=true;
iIdxInput+=strlen(caKeyword);
while(caInput[iIdxInput]==' ') { iIdxInput++; } // remove spaces
break;
}
iIdxInput++;
if(iIdxInput>=(iCharArrayMax-1)) { return false; }
}
if(booFound==false) { return false; }
while(true)
{
if(caInput[iIdxInput]=='\0') { break; }
if(caInput[iIdxInput]=='\r' || caInput[iIdxInput]=='\n') { break; }
if(iIdxOutput<(iOutputMax-1))
{
caOutput[iIdxOutput]=caInput[iIdxInput];
iIdxOutput++;
}
else
{
break;
}
iIdxInput++;
if(iIdxInput>=iCharArrayMax) { break; }
}
caOutput[iIdxOutput]='\0';
if(iIdxOutput<=28) { return false; }
if(caOutput[11]!=',' || caOutput[13]!=',' || caOutput[26]!=',' || caOutput[28]!=',') { return false; }
return true;
}
bool getBooSafeChar(char c1)
{
if ((c1 >= 'a' && c1 <= 'z') ||
(c1 >= 'A' && c1 <= 'Z') ||
(c1 >= '0' && c1 <= '9'))
{
return true;
}
if (c1 == '-' ||
c1 == '_' ||
c1 == '.' ||
c1 == '~')
{
return true;
}
return false;
}
void encodeURIComponent(const char caInput[], char caOutput[], int iOutputMax)
{
static const char caHex[] = "0123456789ABCDEF";
int iIdxInput=0;
int iIdxOutput=0;
while(caInput[iIdxInput]!='\0' && iIdxOutput<(iOutputMax-1))
{
char c1=caInput[iIdxInput];
if(c1=='\r' || c1=='\n')
{
}
else if(getBooSafeChar(c1)==true)
{
caOutput[iIdxOutput]=c1;
iIdxOutput++;
}
else
{
if((iIdxOutput+3)>=iOutputMax) { break; }
caOutput[iIdxOutput+0] = '%';
caOutput[iIdxOutput+1] = caHex[(c1 >> 4) & 0xF];
caOutput[iIdxOutput+2] = caHex[c1 & 0xF];
iIdxOutput+=3;
}
iIdxInput++;
if(iIdxInput>=iCharArrayMax) { break; }
}
caOutput[iIdxOutput]='\0';
}
void sendGpsData(const char caPhp[], const char caGpsData[])
{
static char caEncodedGpsData[iCharArrayMax];
static char caCommand[1024];
caEncodedGpsData[0]='\0';
caCommand[0]='\0';
while(true)
{
if(getBooQA("AT+CPIN?", "+CPIN: READY")==false) { break; }
if(getBooQA("AT+CGATT=1", "\r\nOK\r\n")==false) { break; }
if(getBooQA("AT+CGATT?", "+CGATT: 1")==false) { break; }
if(getBooQA("AT+HTTPINIT", "\r\nOK\r\n")==false) { break; }
// parameters
encodeURIComponent(caGpsData, caEncodedGpsData, iCharArrayMax);
snprintf(
caCommand,
sizeof(caCommand),
"AT+HTTPPARA=\"URL\",\"http://liujiaje.com/resources/20260513_lilygo_t_sim7600g_h/%s?id=%s&data=%s\"",
caPhp, "lilygo1", caEncodedGpsData);
if(getBooQA(caCommand, "\r\nOK\r\n")==false) { break; }
if(getBooQA("AT+HTTPACTION=0", "+HTTPACTION:")==true)
{
delay(50);
getBooQA("AT+HTTPREAD=0,200", "+HTTPREAD:"); // read 200 bytes
delay(50);
}
int iRetry=0;
while(getBooQA("AT+HTTPTERM", "\r\nOK\r\n")==false)
{
delay(1000);
iRetry++;
if(iRetry>30)
{
// modem might be stuck
digitalWrite(4, LOW);
delay(500);
digitalWrite(4, HIGH);
delay(8000);
break;
}
}
break;
}
}
void loop()
{
char caAnswer[iCharArrayMax];
char caGpsData[iCharArrayMax];
iLedVal=(iLedVal+1) % 2;
digitalWrite(iLed, iLedVal);
while(true)
{
if(getBooQA("AT+CGPS?", "+CGPS: 1,1")==false) { break; }
delay(10);
qa(caATCmd, caAnswer);
caAnswer[iCharArrayMax-1]='\0';
if(getBooExtractGPSDataOk(caAnswer, caGpsData, sizeof(caGpsData), caATKeyword)==false) { break; }
if(strlen(caGpsData)<=14) { break; }
sendGpsData(caPhp, caGpsData);
break;
}
delay(1000);
/*
while (Serial.available()) { SerialAT.write(Serial.read()); }
while (SerialAT.available()) { Serial.write(SerialAT.read()); }
*/
}
#define iModemRx 26
#define iModemTx 27
#define iCharArrayMax 1024
#include <ctype.h>
HardwareSerial SerialAT(1);
char caATCmd[]="AT+CGPSINFO";
char caATKeyword[]="+CGPSINFO:";
char caPhp[]="cgpsinfo.php";
/*
char caATCmd[]="AT+CGNSINF";
char caATKeyword[]="+CGNSINF:";
char caPhp[]="cgnsinf.php";
*/
void qa(const char caQuestion[], char caAnswer[])
{
while(SerialAT.available()) { SerialAT.read(); } // clear leftover data
SerialAT.write(caQuestion);
SerialAT.write("\r\n");
delay(50);
int iLen=0;
unsigned long ulLast=millis();
// maybe multi lines
while((millis()-ulLast)<10000)
{
while(SerialAT.available())
{
char c1=SerialAT.read();
ulLast=millis();
if(iLen<(iCharArrayMax-1))
{
caAnswer[iLen]=c1;
iLen++;
caAnswer[iLen]='\0';
}
}
if(strstr(caAnswer, "OK") || strstr(caAnswer, "ERROR")) { break; }
}
delay(50);
}
bool getBooQA(const char caQuestion[], const char caKeyword[])
{
char caAnswerBuffer[iCharArrayMax];
qa(caQuestion, caAnswerBuffer);
if(strstr(caAnswerBuffer, caKeyword)!=NULL) { return true; }
return false;
}
void setup()
{
char caAnswer[iCharArrayMax];
Serial.begin(115200); // esp32
SerialAT.begin(115200, SERIAL_8N1, iModemRx, iModemTx); // sim7600g-h
pinMode(4, OUTPUT);
digitalWrite(4, HIGH);
delay(3000);
while(getBooQA("AT", "OK")==false) { delay(1000); } // wait until AT is OK
// enable GPS via AT+CGPS=1
qa("AT+CGPS=0", caAnswer);
delay(3000);
qa("AT+CGPS=1", caAnswer);
while(getBooQA("AT+CGDCONT=1,\"IP\",\"internet\"", "OK")==false) { delay(1000); } // network registration
}
bool getBooExtractGPSDataOk(char caInput[], char caOutput[], int iOutputMax, const char caKeyword[])
{
int iIdxInput=0;
int iIdxOutput=0;
bool booFound=false;
while(true)
{
if(caInput[iIdxInput]=='\0') { break; }
if(strncmp(&caInput[iIdxInput], caKeyword, strlen(caKeyword))==0)
{
booFound=true;
iIdxInput+=strlen(caKeyword);
while(caInput[iIdxInput]==' ') { iIdxInput++; } // remove spaces
break;
}
iIdxInput++;
}
if(booFound==false) { return false; }
while(true)
{
if(caInput[iIdxInput]=='\0') { break; }
if(caInput[iIdxInput]=='\r' || caInput[iIdxInput]=='\n') { break; }
if(iIdxOutput<(iOutputMax-1))
{
caOutput[iIdxOutput]=caInput[iIdxInput];
iIdxOutput++;
}
iIdxInput++;
}
caOutput[iIdxOutput]='\0';
if(iIdxOutput<=28) { return false; }
if(caOutput[11]!=',' || caOutput[13]!=',' || caOutput[26]!=',' || caOutput[28]!=',') { return false; }
return true;
//return (iIdxOutput>0);
}
bool getBooSafeChar(char c1)
{
if ((c1 >= 'a' && c1 <= 'z') ||
(c1 >= 'A' && c1 <= 'Z') ||
(c1 >= '0' && c1 <= '9'))
{
return true;
}
if (c1 == '-' ||
c1 == '_' ||
c1 == '.' ||
c1 == '~')
{
return true;
}
return false;
}
void encodeURIComponent(const char caInput[], char caOutput[], int iOutputMax)
{
int iIdxInput=0;
int iIdxOutput=0;
while(caInput[iIdxInput]!='\0' && iIdxOutput<(iOutputMax-1))
{
char c1=caInput[iIdxInput];
if(c1=='\r' || c1=='\n')
{
}
else if(getBooSafeChar(c1)==true)
{
caOutput[iIdxOutput]=c1;
iIdxOutput++;
}
else
{
if((iIdxOutput+3)>=iOutputMax) { break; }
sprintf(&caOutput[iIdxOutput], "%%%02X", (unsigned char)c1);
iIdxOutput+=3;
}
iIdxInput++;
}
caOutput[iIdxOutput]='\0';
}
void sendGpsData(const char caPhp[], const char caGpsData[])
{
char caEncodedGpsData[iCharArrayMax];
while(true)
{
if(getBooQA("AT+CPIN?", "+CPIN: READY")==false) { break; }
if(getBooQA("AT+CGATT=1", "OK")==false) { break; }
if(getBooQA("AT+CGATT?", "+CGATT: 1")==false) { break; }
if(getBooQA("AT+HTTPINIT", "OK")==false) { break; }
// parameters
encodeURIComponent(caGpsData, caEncodedGpsData, iCharArrayMax);
char caCommand[1024];
snprintf(
caCommand,
sizeof(caCommand),
"AT+HTTPPARA=\"URL\",\"http://liujiaje.com/resources/20260513_lilygo_t_sim7600g_h/%s?id=%s&data=%s\"",
caPhp, "lilygo1", caEncodedGpsData);
if(getBooQA(caCommand, "OK")==false) { break; }
if(getBooQA("AT+HTTPACTION=0", "OK")==true)
{
delay(50);
getBooQA("AT+HTTPREAD=0,200", "."); // read 200 bytes
delay(50);
}
while(getBooQA("AT+HTTPTERM", "OK")==false) { delay(1000); }
// delay(500);
break;
}
}
void loop()
{
char caAnswer[iCharArrayMax];
char caGpsData[iCharArrayMax];
while(true)
{
if(getBooQA("AT+CGPS?", "+CGPS: 1,1")==false) { break; }
delay(10);
qa(caATCmd, caAnswer);
if(getBooExtractGPSDataOk(caAnswer, caGpsData, sizeof(caGpsData), caATKeyword)==false) { break; }
if(strlen(caGpsData)<=14) { break; }
sendGpsData(caPhp, caGpsData);
// delay(4000);
break;
}
delay(1000);
/*
while (Serial.available()) { SerialAT.write(Serial.read()); }
while (SerialAT.available()) { Serial.write(SerialAT.read()); }
*/
}