一、運行效果展示
1.1 windows系統運行效果展示
網路攝像頭專案(Windows系統運行效果)
1.2 Android系統運行效果展示
網路攝像頭專案(Android系統運行效果)
1.3 Linux系統運行效果展示
網路攝像頭專案(Linux系統運行效果)
二、功能簡介
2.1 功能介紹
這是基于C++(QT框架)設計的網路攝像頭專案,本篇文章介紹的網路攝像頭專案并不是采用RTMP或者RTSP推流編碼的網路攝像頭產品,而是采用HTTP協議推送圖片流的方式,采用瀏覽器訪問查看攝像頭畫面,
專案原始碼下載地址: https://download.csdn.net/download/xiaolong1126626497/21232910
這是軟體運行截圖:

這是瀏覽器訪問截圖:

專案運行的效果:
軟體打開之后,先點擊重繪資訊,程式會掃描當前設備的攝像頭和IP地址資訊;并將訪問的地址列印了出來,然后點擊開啟攝像頭,界面就實時顯示選擇的攝像頭畫面, 在局域網內,其他設備打開瀏覽器,輸入下面提示的地址訪問,輸入用戶名和密碼,即可查看到攝像頭畫面,程式里處理瀏覽器的請求是采用多執行緒方式,可以支持多個瀏覽器同時訪問,
代碼思路
代碼采用的是C++(QT框架)撰寫,代碼本身主要是分為兩個部分:
1. 系結指定埠號,創建TCP服務器,用來回應客戶端的請求(瀏覽器)
2. 攝像頭畫面采集部分,攝像頭資料采集采用單獨的執行緒,采集之后將影像傳遞給界面重繪顯示,并將影像填充到全域緩沖區,方便客戶端處理執行緒將影像再傳遞給瀏覽器,
每當有新的瀏覽器訪問進來,就會單獨開辟一個執行緒,去處理這個瀏覽器接下來的通信互動,瀏覽器斷開連接之后,執行緒自動銷毀;影像緩沖區是一個公共的緩沖區,攝像頭影像采集執行緒向該緩沖區填充資料,與瀏覽器通信的執行緒就從這個緩沖區讀取資料,采用的是條件變數+互斥鎖同步訪問,
專案里用到的知識點主要是攝像頭采集,執行緒處理、網路編程,HTTP協議等知識點,
如果是搞QT開發,都可以當做入門學習參考;如果想要用其他語言實作,思路搞清楚也很容易, 標準C語言,
在Linux下如果不需要界面,可以直接使用C語言完成專案效果,攝像頭采集采用Linux下標準V4L2框架,執行緒就采用pthread_create創建,互斥鎖、條件變數這些Linux都有,只要把程式思路搞清楚,實作起來還是很容易,
2.2 跨平臺運行
代碼是采用QT框架撰寫,所以支持跨平臺編譯運行;目前代碼在Android、Linux、windows系統上都編譯運行通過,達到相同效果;由于身邊沒有蘋果設備,暫時未做測驗,
不同系統間代碼上還是有少許區別,代碼里通過宏的方式區分了,不同的系統運行不同的部分代碼,
如果要編譯QT的代碼在Android設備上運行,得先搭建對應的開發環境;如果不會環境搭建,可以參考下面鏈接,
(1). Linux(ubuntu)下搭建QT Android開發環境
https://blog.csdn.net/xiaolong1126626497/article/details/117256660
(2). windows(win10 64bit)下搭建QT Android開發環境
https://blog.csdn.net/xiaolong1126626497/article/details/117254453
(3). windows下Qt + VS2017 環境安裝
https://blog.csdn.net/xiaolong1126626497/article/details/112402861
2.3 專案原始碼運行環境
QT版本: 5.12.6
編譯器: MinGW 32
系統: Win10 Ubuntu18.04
2.4 代碼運行介紹
程式運行時,需要用到一些資源檔案,這些資源檔案在程式的原始碼目錄下,名稱是: www,

如果是windows、Linux系統環境,需要把資源目錄拷貝到程式運行的同級目錄下,
如果是Android系統,這些資源檔案需要在編譯的時候打包進APK里,在工程目錄下的Android目錄里創建一個assets目錄,將資源檔案全部拷貝到assets目錄下;程式編譯的時候會自動將assets目錄打包進apk,到時候Android程式運行時就可以直接去assets目錄下獲取資源檔案,

當apk生成之后,可以解壓出來看看,assets目錄打包成功沒有,


檢查了下,打包成功的,這樣Android程式運行時,訪問的路徑就沒有問題,
2.5 Linux系統運行攝像頭無法讀取資料問題說明
由于我測驗的環境是在虛擬機里運行ubuntu系統,要在ubuntu里訪問攝像頭,需要先把攝像頭掛載到虛擬機,才能訪問,如果打開虛擬機發現右下角托盤里攝像頭圖示都沒有,說明托盤程式可能被360、電腦管家之類的安全軟體禁止了,需要開啟自動啟動,重啟VM虛擬機軟體再測驗,

直接掛載進來可能遇到攝像頭打開了,但是影像無法獲取的情況,這個問題可能是USB協議版本引起的,
可以在這里切換USB協議測驗,我的筆記本電腦是USB3.0才可以正常使用,

三、代碼分析

3.1 初始化代碼(建構式)
代碼開發時,主要是針對在windows平臺運行的,所有程式里很多都是偏向于windows環境的設計,
建構式里去除了系統原視窗標題欄,自定義了自己的標題;QT隱藏標題欄之后,是不能拖動拉伸的,需要自己實作,我這里采用的是GitHUB上開源的一個示例代碼實作的這部分功能,效果不錯,達到了想要的效果,剩下代碼里初始化了托盤系統,方便程式最小化時,隱藏在windows系統的托盤圖示欄里,其他的代碼就是做寫基本的初始化,信號槽的連接,IP地址、攝像頭資訊重繪等,
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//自定義標題欄
FramelessHelper *pHelper = new FramelessHelper(this);
setWindowFlags(Qt::FramelessWindowHint);
pHelper->activateOn(this); //激活當前表單
//pHelper->setTitleHeight(m_pTitleBar->height()); //設定表單的標題欄高度
pHelper->setWidgetMovable(true); //設定表單可移動
pHelper->setWidgetResizable(true); //設定表單可縮放
// pHelper->setRubberBandOnMove(true); //設定橡皮筋效果-可移動
// pHelper->setRubberBandOnResize(true); //設定橡皮筋效果-可縮放
/*基本設定*/
this->setWindowIcon(QIcon(":/log.ico")); //設定圖示
this->setWindowTitle("網路攝像頭");
//打開的視窗在螢屏中間
QDesktopWidget *widget= QApplication::desktop();
move((widget->width()-this->width())/2,(widget->height()-this->height())/2);
//重繪攝像頭與IP地址資訊
on_pushButton_config_clicked();
//重繪在線人數
timer = new QTimer(this);
connect(timer, SIGNAL(timeout()), this, SLOT(timer_update()));
timer->start(1000);
tray= new QSystemTrayIcon(this);//初始化托盤物件tray
connect(tray,SIGNAL(activated(QSystemTrayIcon::ActivationReason)),this,SLOT(show_Widget(QSystemTrayIcon::ActivationReason)));
tray->setIcon(QIcon(QPixmap(":/log.png")));//設定托盤圖示,引號內是自定義的png圖片路徑
tray->setToolTip("網路攝像頭"); //提示文字
restoreAction = new QAction("打開", this);
connect(restoreAction, SIGNAL(triggered()), this, SLOT(show()));
quitAction = new QAction("退出", this);
connect(quitAction, SIGNAL(triggered()), qApp, SLOT(quit()));
trayMenu = new QMenu(this);
trayMenu->addAction(restoreAction);
trayMenu->addSeparator();
trayMenu->addAction(quitAction);
tray->setContextMenu(trayMenu);
work_class=new VideoReadThread_0;
work_thread=new QThread;
//啟動攝像頭的信號
connect(this,SIGNAL(StartWorkThread()),work_class,SLOT(run()));
//釋放資源-釋放攝像頭
connect(this,SIGNAL(Stop_VideoAudioEncode_0()),work_class,SLOT(stop()));
//連接攝像頭采集信號,在主執行緒實時顯示視頻畫面
connect(work_class,SIGNAL(VideoDataOutput(QImage)),this,SLOT(VideoDataDisplay_0(QImage)));
//將類移動到子執行緒作業
work_class->moveToThread(work_thread);
}
3.2 攝像頭采集部分
攝像頭采集采用的是QCamera + QVideoProbe實作,
初始化代碼示例: 初始化代碼里完成攝像頭的一些引數,捕獲模式,槽函式關聯等設定,
初始化代碼默認設定輸出的影像格式是YUYV,在windows和Linux系統上是支持的,這個可能與攝像頭有關,實際需要測驗調整;Android系統上只支持NV21格式,如果是Android系統上運行,要記得修改格式,
void VideoReadThread_0::Camear_Init()
{
/*創建攝像頭物件,根據選擇的攝像頭打開*/
camera = new QCamera(videoaudioencode_0.camera);
m_pProbe = new QVideoProbe;
if(m_pProbe != nullptr)
{
m_pProbe->setSource(camera); // Returns true, hopefully.
connect(m_pProbe, SIGNAL(videoFrameProbed(QVideoFrame)),this, SLOT(slotOnProbeFrame(QVideoFrame)), Qt::QueuedConnection);
}
else
{
qDebug()<<"m_pProbe == nullptr";
}
/*配置攝像頭捕 QCamera *camera;
QVideoProbe *m_pProbe;獲模式為幀捕獲模式*/
camera->setCaptureMode(QCamera::CaptureVideo); //如果在Linux系統下運行就這樣設定
//camera->setCaptureMode(QCamera::CaptureVideo);//如果在android系統下運行就這樣設定
/*設定攝像頭的采集幀率和解析度*/
QCameraViewfinderSettings settings;
settings.setPixelFormat(QVideoFrame::Format_YUYV); //設定像素格式 Android上只支持NV21格式
settings.setResolution(QSize(VIDEO_WIDTH,VIDEO_HEIGHT)); //設定攝像頭的解析度
camera->setViewfinderSettings(settings);
/*啟動攝像頭*/
camera->start();
}
捕獲到攝像頭的影像資料之后,QVideoProbe會發出videoFrameProbed信號,在關聯的槽函式里完成資料處理,
處理代碼如下:
void VideoReadThread_0::slotOnProbeFrame(const QVideoFrame &frame)
{
// qDebug()<<"開始采集.";
QVideoFrame cloneFrame(frame);
cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
// qDebug()<<"height:"<<cloneFrame.height();
// qDebug()<<"width:"<<cloneFrame.width();
// qDebug()<<"bytesPerLine:"<<cloneFrame.bytesPerLine();
// qDebug()<<"mappedBytes:"<<cloneFrame.mappedBytes();
// qDebug()<<"pixelFormat:"<<cloneFrame.pixelFormat();
unsigned char rgb_buffer[VIDEO_WIDTH*VIDEO_HEIGHT*3];
if(cloneFrame.pixelFormat()==QVideoFrame::Format_YUYV)
{
yuyv_to_rgb(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height());
}
else if(cloneFrame.pixelFormat()==QVideoFrame::Format_NV21)
{
NV21_TO_RGB24(cloneFrame.bits(),rgb_buffer,cloneFrame.width(),cloneFrame.height());
}
else
{
qDebug()<<"當前格式編碼為%1,暫時不支持轉換.\n";
return;
}
cloneFrame.unmap();
//加載圖片資料
QImage image(rgb_buffer,
cloneFrame.width(),
cloneFrame.height(),
QImage::Format_RGB888);
//繪制圖片水印
QDateTime dateTime(QDateTime::currentDateTime());
//時間效果: 2020-03-05 16:25::04 周一
QString qStr="";
qStr+=dateTime.toString("yyyy-MM-dd hh:mm:ss ddd");
QPainter pp(&image);
QPen pen = QPen(Qt::white);
pp.setPen(pen);
pp.setFont(QFont("宋體",20));
pp.drawText(QPointF(0,40),qStr);
mutex.lock();
QBuffer buff;
image.save(&buff,"jpg",80); // 30表示壓宿率,值從0 – 100, 值越小表示編碼出來的影像檔案就越小,當然也就越不清晰
lcd_image_data=buff.data();
cond_wait.wakeAll();
mutex.unlock();
emit VideoDataOutput(image); //發送信號
}
代碼里區分了資料格式,如果是YUYV就呼叫對應的轉換函式將資料轉為RGB格式,如果是NV21也是一樣,呼叫對應的轉換函式將資料轉為RGB格式后續再做其他處理,
影像格式轉換的代碼如下:
/*
函式功能: 將YUV資料轉為RGB格式
函式引數:
unsigned char *yuv_buffer: YUV源資料
unsigned char *rgb_buffer: 轉換之后的RGB資料
int iWidth,int iHeight : 影像的寬度和高度
*/
void yuyv_to_rgb(unsigned char *yuv_buffer,unsigned char *rgb_buffer,int iWidth,int iHeight)
{
int x;
int z=0;
unsigned char *ptr = rgb_buffer;
unsigned char *yuyv= yuv_buffer;
for (x = 0; x < iWidth*iHeight; x++)
{
int r, g, b;
int y, u, v;
if (!z)
y = yuyv[0] << 8;
else
y = yuyv[2] << 8;
u = yuyv[1] - 128;
v = yuyv[3] - 128;
r = (y + (359 * v)) >> 8;
g = (y - (88 * u) - (183 * v)) >> 8;
b = (y + (454 * u)) >> 8;
*(ptr++) = (r > 255) ? 255 : ((r < 0) ? 0 : r);
*(ptr++) = (g > 255) ? 255 : ((g < 0) ? 0 : g);
*(ptr++) = (b > 255) ? 255 : ((b < 0) ? 0 : b);
if(z++)
{
z = 0;
yuyv += 4;
}
}
}
void NV21_TO_RGB24(unsigned char *yuyv, unsigned char *rgb, int width, int height)
{
const int nv_start = width * height ;
int index = 0, rgb_index = 0;
uint8_t y, u, v;
int r, g, b, nv_index = 0,i, j;
for(i = 0; i < height; i++){
for(j = 0; j < width; j ++){
//nv_index = (rgb_index / 2 - width / 2 * ((i + 1) / 2)) * 2;
nv_index = i / 2 * width + j - j % 2;
y = yuyv[rgb_index];
u = yuyv[nv_start + nv_index ];
v = yuyv[nv_start + nv_index + 1];
r = y + (140 * (v-128))/100; //r
g = y - (34 * (u-128))/100 - (71 * (v-128))/100; //g
b = y + (177 * (u-128))/100; //b
if(r > 255) r = 255;
if(g > 255) g = 255;
if(b > 255) b = 255;
if(r < 0) r = 0;
if(g < 0) g = 0;
if(b < 0) b = 0;
index = rgb_index % width + (height - i - 1) * width;
//rgb[index * 3+0] = b;
//rgb[index * 3+1] = g;
//rgb[index * 3+2] = r;
//顛倒影像
//rgb[height * width * 3 - i * width * 3 - 3 * j - 1] = b;
//rgb[height * width * 3 - i * width * 3 - 3 * j - 2] = g;
//rgb[height * width * 3 - i * width * 3 - 3 * j - 3] = r;
//正面影像
rgb[i * width * 3 + 3 * j + 0] = b;
rgb[i * width * 3 + 3 * j + 1] = g;
rgb[i * width * 3 + 3 * j + 2] = r;
rgb_index++;
}
}
}
影像格式轉換完成之后,下面就加載到QImage里,繪制上時間水印,將資料填充到緩沖區,然后再喚醒等待的執行緒,最后在傳遞一份資料給UI界面完成渲染顯示,
如果使用程序中,攝像頭支持的是其他格式,那么這里需要自己增加對應的轉換函式,
3.3 瀏覽器互動執行緒
這里是TCP客戶端處理資料的執行緒,每當連接一個新的客戶端,就會開辟一個新的執行緒獨立運行,
執行緒里完成瀏覽器請求的處理,回應,互動,
完整的回應代碼如下:
void TcpServerThread::run()
{
qDebug()<<"TcpServerThread_子執行緒run槽函式的ID:"<<QThread::currentThreadId();
QTcpSocket *tcpSocket;
tcpSocket=new QTcpSocket;
/*使用socketDescriptor套接字初始化QAbstractSocket*/
if(!tcpSocket->setSocketDescriptor(socketDescriptor))
{
return;
}
//讀取接收的資料
QString text;
if(tcpSocket->waitForReadyRead())
{
text=tcpSocket->readAll();
}
//處理瀏覽器的請求
if(text.contains("GET / HTTP/1.1", Qt::CaseInsensitive))
{
//如果是Android系統
#ifdef Q_OS_ANDROID
text="assets:/login.html";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/login.html";
#endif
SendFileData(buff,text,"text/html",tcpSocket);
}
else if(text.contains("GET /image.jpg HTTP/1.1", Qt::CaseInsensitive))
{
SendImageData(buff,tcpSocket);
}
else if(text.contains("GET /favicon.ico HTTP/1.1", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/logo.ico";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/logo.ico";
#endif
SendFileData(buff,text,"image/x-icon",tcpSocket);
}
else if(text.contains("GET /three.min.js", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/three.min.js";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/three.min.js";
#endif
SendFileData(buff,text,"application/x-javascript",tcpSocket);
}
else if(text.contains("GET /style.css", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/style.css";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/style.css";
#endif
SendFileData(buff,text,"text/css",tcpSocket);
}
else if(text.contains("GET /Stats.min.js", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/Stats.min.js";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/Stats.min.js";
#endif
SendFileData(buff,text,"application/x-javascript",tcpSocket);
}
else if(text.contains("GET /logo.png HTTP/1.1", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/logo.png";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/logo.png";
#endif
SendFileData(buff,text,"image/png",tcpSocket);
}
else if(text.contains("userName=admin&passWord=12345678", Qt::CaseInsensitive))
{
#ifdef Q_OS_ANDROID
text="assets:/index.html";
#else
text=QCoreApplication::applicationDirPath();
text+="/www/index.html";
#endif
SendFileData(buff,text,"text/html",tcpSocket);
}
//卸載連接的套接字
fd_list.Del_fd(socketDescriptor);
//關閉連接
tcpSocket->close();
delete tcpSocket;
}
/**
* 向瀏覽器回應請求資料
*/
int TcpServerThread::SendFileData(char *buff, QString file_path, const char *type, QTcpSocket *tcpSocket)
{
qint64 size=0;
/*1. 讀取檔案*/
QFile file(file_path);
if(file.open(QIODevice::ReadOnly)!=true)
{
qDebug()<<"檔案不存在:"<<file_path<<endl;
return -1;
}
size=file.size(); //得到檔案大小
QByteArray byte=file.readAll(); //讀取所有資料
file.close();//關閉檔案
/*2. 構建回應格式字串*/
sprintf(buff,"HTTP/1.1 200 OK\r\n"
"Content-type:%s\r\n"
"Content-Length:%lld\r\n"
"\r\n",type,size);
/*3. 向客戶端發送回應請求*/
if(tcpSocket->write(buff,strlen(buff))<=0)return -2;
tcpSocket->waitForBytesWritten(); //等待寫
/*4. 向客戶端發送回應物體資料*/
if(tcpSocket->write(byte)<=0)return -3;
tcpSocket->waitForBytesWritten(); //等待寫
return 0;
}
/*
向客戶端回圈發送圖片資料流
*/
int TcpServerThread::SendImageData(char *buff, QTcpSocket *tcpSocket)
{
/*1. 構建回應格式字串*/
sprintf(buff,"HTTP/1.0 200 OK\r\n"
"Server: wbyq\r\n"
"Content-Type:multipart/x-mixed-replace;boundary=boundarydonotcross\r\n"
"\r\n"
"--boundarydonotcross\r\n");
/*2. 向客戶端發送回應請求*/
if(tcpSocket->write(buff,strlen(buff))<=0)return -2;
tcpSocket->waitForBytesWritten(); //等待寫
/*3. 回圈發送資料*/
while(thread_run_flag)
{
//從全域緩沖區取資料
mutex.lock();
cond_wait.wait(&mutex);
QByteArray image_data; //保存一幀影像資料
image_data=lcd_image_data;
mutex.unlock();
// QBuffer data_buff;
// QPixmap pixmap;
// QScreen *screen = QGuiApplication::primaryScreen();
// pixmap=screen->grabWindow(0); //獲取當前螢屏的影像
// //縮放影像
// pixmap = pixmap.scaled(lcd_image_w,lcd_image_h, Qt::KeepAspectRatio);
// pixmap.save(&data_buff,"jpg",image_val);
// QByteArray image_data1=data_buff.data();
/*4. 向瀏覽器發送回應頭*/
sprintf(buff,"Content-type:image/jpeg\r\n"
"Content-Length:%d\r\n"
"\r\n",image_data.size());
if(tcpSocket->write(buff,strlen(buff))<=0)break;
tcpSocket->waitForBytesWritten(); //等待寫
/*5. 發送物體資料*/
if(tcpSocket->write(image_data)<=0)break;
tcpSocket->waitForBytesWritten(); //等待寫
/*6. 發送邊界符*/
sprintf(buff,"\r\n--boundarydonotcross\r\n");
if(tcpSocket->write(buff,strlen(buff))<=0)break;
tcpSocket->waitForBytesWritten(); //等待寫
//等待一段時間
msleep(5);
}
return 0;
}
當密碼、賬號驗證通過之后,服務器就與瀏覽器建立長連接,回圈發送圖片流資料,瀏覽器完成連續重繪顯示, 里面實作的思路,就是基本的TCP網路編程代碼,
四、 HTTP協議簡單介紹
這個網路攝像頭專案主要是與瀏覽器互動的,要完成瀏覽器的互動,首先得知道HTTP協議的報文格式,如何回應,下面只是介紹當前專案里用到的部分知識,方便理解第三章的瀏覽器互動代碼,
4.1 HTTP協議介紹
HTTP協議是Hyper Text Transfer Protocol(超文本傳輸協議)的縮寫,服務器傳輸超文本到本地瀏覽器的傳送協議,
HTTP是一個基于TCP/IP通信協議來傳遞資料(HTML 檔案, 圖片檔案, 查詢結果等),
HTTP是無狀態:HTTP協議是無狀態協議,無狀態是指協議對于事務處理沒有記憶能力,缺少狀態意味著如果后續處理需要前面的資訊,則它必須重傳,這樣可能導致每次連接傳送的資料量增大,另一方面,在服務器不需要先前資訊時它的應答就較快,
HTTP協議作業于客戶端-服務端架構上,瀏覽器作為HTTP客戶端通過URL向HTTP服務端即WEB服務器發送所有請求,Web服務器根據接收到的請求后,向客戶端發送回應資訊,

4.2 請求方法與報文格式
客戶端請求訊息
客戶端發送一個HTTP請求到服務器的請求訊息包括以下格式:請求行(request line)、請求頭部(header)、空行和請求資料四個部分組成,下圖給出了請求報文的一般格式,
服務器回應訊息
HTTP回應也由四個部分組成,分別是:狀態行、訊息報頭、空行和回應正文,

HTTP協議常用的2種請求方法: GET, POST 方法,
| 1 | GET | 請求指定的頁面資訊,并回傳物體主體, |
| 2 | POST | 向指定資源提交資料進行處理請求(例如提交表單或者上傳檔案),資料被包含在請求體中,POST請求可能會導致新的資源的建立和/或已有資源的修改, |
4.3 HTTP回應頭資訊
HTTP請求頭提供了關于請求,回應或者其他的發送物體的資訊,
| 應答頭 | 說明 |
| Allow | 服務器支持哪些請求方法(如GET、POST等), |
| Content-Encoding | 檔案的編碼(Encode)方法,只有在解碼之后才可以得到Content-Type頭指定的內容型別,利用gzip壓縮檔案能夠顯著地減少HTML檔案的下載時間,Java的GZIPOutputStream可以很方便地進行gzip壓縮,但只有Unix上的Netscape和Windows上的IE 4、IE 5才支持它,因此,Servlet應該通過查看Accept-Encoding頭(即request.getHeader("Accept-Encoding"))檢查瀏覽器是否支持gzip,為支持gzip的瀏覽器回傳經gzip壓縮的HTML頁面,為其他瀏覽器回傳普通頁面, |
| Content-Length | 表示內容長度,只有當瀏覽器使用持久HTTP連接時才需要這個資料,如果你想要利用持久連接的優勢,可以把輸出檔案寫入 ByteArrayOutputStream,完成后查看其大小,然后把該值放入Content-Length頭,最后通過byteArrayStream.writeTo(response.getOutputStream()發送內容, |
| Content-Type | 表示后面的檔案屬于什么MIME型別,Servlet默認為text/plain,但通常需要顯式地指定為text/html,由于經常要設定Content-Type,因此HttpServletResponse提供了一個專用的方法setContentType, |
| Date | 當前的GMT時間,你可以用setDateHeader來設定這個頭以避免轉換時間格式的麻煩, |
| Expires | 應該在什么時候認為檔案已經過期,從而不再快取它? |
| Last-Modified | 檔案的最后改動時間,客戶可以通過If-Modified-Since請求頭提供一個日期,該請求將被視為一個條件GET,只有改動時間遲于指定時間的檔案才會回傳,否則回傳一個304(Not Modified)狀態,Last-Modified也可用setDateHeader方法來設定, |
| Location | 表示客戶應當到哪里去提取檔案,Location通常不是直接設定的,而是通過HttpServletResponse的sendRedirect方法,該方法同時設定狀態代碼為302, |
| Refresh | 表示瀏覽器應該在多少時間之后重繪檔案,以秒計,除了重繪當前檔案之外,你還可以通過setHeader("Refresh", "5; URL=http://host/path")讓瀏覽器讀取指定的頁面, 注意Refresh的意義是"N秒之后重繪本頁面或訪問指定頁面",而不是"每隔N秒重繪本頁面或訪問指定頁面",因此,連續重繪要求每次都發送一個Refresh頭,而發送204狀態代碼則可以阻止瀏覽器繼續重繪,不管是使用Refresh頭還是<META HTTP-EQUIV="Refresh" ...>, |
| Server | 服務器名字,Servlet一般不設定這個值,而是由Web服務器自己設定, |
| Set-Cookie | 設定和頁面關聯的Cookie,Servlet不應使用response.setHeader("Set-Cookie", ...),而是應使用HttpServletResponse提供的專用方法addCookie,參見下文有關Cookie設定的討論, |
| WWW-Authenticate | 客戶應該在Authorization頭中提供什么型別的授權資訊?在包含401(Unauthorized)狀態行的應答中這個頭是必需的,例如,response.setHeader("WWW-Authenticate", "BASIC realm=\"executives\""), |
4.4 HTTP狀態碼
當瀏覽者訪問一個網頁時,瀏覽者的瀏覽器會向網頁所在服務器發出請求,
當瀏覽器接收并顯示網頁前,此網頁所在的服務器會回傳一個包含HTTP狀態碼的資訊頭(server header)用以回應瀏覽器的請求,
HTTP狀態碼的英文為HTTP Status Code,
下面是常見的HTTP狀態碼:
200 - 請求成功
301 - 資源(網頁等)被永久轉移到其它URL
404 - 請求的資源(網頁等)不存在
500 - 內部服務器錯誤
4.5 HTTP content-type
Content-Type,內容型別,一般是指網頁中存在的Content-Type,用于定義網路檔案的型別和網頁的編碼,決定瀏覽器將以什么形式、什么編碼讀取這個檔案,這就是經常看到一些Asp網頁點擊的結果卻是下載到的一個檔案或一張圖片的原因,
HTTP content-type 對照表:
| 檔案擴展名 | Content-Type(Mime-Type) | 檔案擴展名 | Content-Type(Mime-Type) |
| .*( 二進制流,不知道下載檔案型別) | application/octet-stream | .tif | image/tiff |
| .001 | application/x-001 | .301 | application/x-301 |
| .323 | text/h323 | .906 | application/x-906 |
| .907 | drawing/907 | .a11 | application/x-a11 |
| .acp | audio/x-mei-aac | .ai | application/postscript |
| .aif | audio/aiff | .aifc | audio/aiff |
| .aiff | audio/aiff | .anv | application/x-anv |
| .asa | text/asa | .asf | video/x-ms-asf |
| .asp | text/asp | .asx | video/x-ms-asf |
| .au | audio/basic | .avi | video/avi |
| .awf | application/vnd.adobe.workflow | .biz | text/xml |
| .bmp | application/x-bmp | .bot | application/x-bot |
| .c4t | application/x-c4t | .c90 | application/x-c90 |
| .cal | application/x-cals | .cat | application/vnd.ms-pki.seccat |
| .cdf | application/x-netcdf | .cdr | application/x-cdr |
| .cel | application/x-cel | .cer | application/x-x509-ca-cert |
| .cg4 | application/x-g4 | .cgm | application/x-cgm |
| .cit | application/x-cit | .class | java/* |
| .cml | text/xml | .cmp | application/x-cmp |
| .cmx | application/x-cmx | .cot | application/x-cot |
| .crl | application/pkix-crl | .crt | application/x-x509-ca-cert |
| .csi | application/x-csi | .css | text/css |
| .cut | application/x-cut | .dbf | application/x-dbf |
| .dbm | application/x-dbm | .dbx | application/x-dbx |
| .dcd | text/xml | .dcx | application/x-dcx |
| .der | application/x-x509-ca-cert | .dgn | application/x-dgn |
| .dib | application/x-dib | .dll | application/x-msdownload |
| .doc | application/msword | .dot | application/msword |
| .drw | application/x-drw | .dtd | text/xml |
| .dwf | Model/vnd.dwf | .dwf | application/x-dwf |
| .dwg | application/x-dwg | .dxb | application/x-dxb |
| .dxf | application/x-dxf | .edn | application/vnd.adobe.edn |
| .emf | application/x-emf | .eml | message/rfc822 |
| .ent | text/xml | .epi | application/x-epi |
| .eps | application/x-ps | .eps | application/postscript |
| .etd | application/x-ebx | .exe | application/x-msdownload |
| .fax | image/fax | .fdf | application/vnd.fdf |
| .fif | application/fractals | .fo | text/xml |
| .frm | application/x-frm | .g4 | application/x-g4 |
| .gbr | application/x-gbr | . | application/x- |
| .gif | image/gif | .gl2 | application/x-gl2 |
| .gp4 | application/x-gp4 | .hgl | application/x-hgl |
| .hmr | application/x-hmr | .hpg | application/x-hpgl |
| .hpl | application/x-hpl | .hqx | application/mac-binhex40 |
| .hrf | application/x-hrf | .hta | application/hta |
| .htc | text/x-component | .htm | text/html |
| .html | text/html | .htt | text/webviewhtml |
| .htx | text/html | .icb | application/x-icb |
| .ico | image/x-icon | .ico | application/x-ico |
| .iff | application/x-iff | .ig4 | application/x-g4 |
| .igs | application/x-igs | .iii | application/x-iphone |
| .img | application/x-img | .ins | application/x-internet-signup |
| .isp | application/x-internet-signup | .IVF | video/x-ivf |
| .java | java/* | .jfif | image/jpeg |
| .jpe | image/jpeg | .jpe | application/x-jpe |
| .jpeg | image/jpeg | .jpg | image/jpeg |
| .jpg | application/x-jpg | .js | application/x-javascript |
| .jsp | text/html | .la1 | audio/x-liquid-file |
| .lar | application/x-laplayer-reg | .latex | application/x-latex |
| .lavs | audio/x-liquid-secure | .lbm | application/x-lbm |
| .lmsff | audio/x-la-lms | .ls | application/x-javascript |
| .ltr | application/x-ltr | .m1v | video/x-mpeg |
| .m2v | video/x-mpeg | .m3u | audio/mpegurl |
| .m4e | video/mpeg4 | .mac | application/x-mac |
| .man | application/x-troff-man | .math | text/xml |
| .mdb | application/msaccess | .mdb | application/x-mdb |
| .mfp | application/x-shockwave-flash | .mht | message/rfc822 |
| .mhtml | message/rfc822 | .mi | application/x-mi |
| .mid | audio/mid | .midi | audio/mid |
| .mil | application/x-mil | .mml | text/xml |
| .mnd | audio/x-musicnet-download | .mns | audio/x-musicnet-stream |
| .mocha | application/x-javascript | .movie | video/x-sgi-movie |
| .mp1 | audio/mp1 | .mp2 | audio/mp2 |
| .mp2v | video/mpeg | .mp3 | audio/mp3 |
| .mp4 | video/mpeg4 | .mpa | video/x-mpg |
| .mpd | application/vnd.ms-project | .mpe | video/x-mpeg |
| .mpeg | video/mpg | .mpg | video/mpg |
| .mpga | audio/rn-mpeg | .mpp | application/vnd.ms-project |
| .mps | video/x-mpeg | .mpt | application/vnd.ms-project |
| .mpv | video/mpg | .mpv2 | video/mpeg |
| .mpw | application/vnd.ms-project | .mpx | application/vnd.ms-project |
| .mtx | text/xml | .mxp | application/x-mmxp |
| .net | image/pnetvue | .nrf | application/x-nrf |
| .nws | message/rfc822 | .odc | text/x-ms-odc |
| .out | application/x-out | .p10 | application/pkcs10 |
| .p12 | application/x-pkcs12 | .p7b | application/x-pkcs7-certificates |
| .p7c | application/pkcs7-mime | .p7m | application/pkcs7-mime |
| .p7r | application/x-pkcs7-certreqresp | .p7s | application/pkcs7-signature |
| .pc5 | application/x-pc5 | .pci | application/x-pci |
| .pcl | application/x-pcl | .pcx | application/x-pcx |
| | application/pdf | | application/pdf |
| .pdx | application/vnd.adobe.pdx | .pfx | application/x-pkcs12 |
| .pgl | application/x-pgl | .pic | application/x-pic |
| .pko | application/vnd.ms-pki.pko | .pl | application/x-perl |
| .plg | text/html | .pls | audio/scpls |
| .plt | application/x-plt | .png | image/png |
| .png | application/x-png | .pot | application/vnd.ms-powerpoint |
| .ppa | application/vnd.ms-powerpoint | .ppm | application/x-ppm |
| .pps | application/vnd.ms-powerpoint | .ppt | application/vnd.ms-powerpoint |
| .ppt | application/x-ppt | .pr | application/x-pr |
| .prf | application/pics-rules | .prn | application/x-prn |
| .prt | application/x-prt | .ps | application/x-ps |
| .ps | application/postscript | .ptn | application/x-ptn |
| .pwz | application/vnd.ms-powerpoint | .r3t | text/vnd.rn-realtext3d |
| .ra | audio/vnd.rn-realaudio | .ram | audio/x-pn-realaudio |
| .ras | application/x-ras | .rat | application/rat-file |
| .rdf | text/xml | .rec | application/vnd.rn-recording |
| .red | application/x-red | .rgb | application/x-rgb |
| .rjs | application/vnd.rn-realsystem-rjs | .rjt | application/vnd.rn-realsystem-rjt |
| .rlc | application/x-rlc | .rle | application/x-rle |
| .rm | application/vnd.rn-realmedia | .rmf | application/vnd.adobe.rmf |
| .rmi | audio/mid | .rmj | application/vnd.rn-realsystem-rmj |
| .rmm | audio/x-pn-realaudio | .rmp | application/vnd.rn-rn_music_package |
| .rms | application/vnd.rn-realmedia-secure | .rmvb | application/vnd.rn-realmedia-vbr |
| .rmx | application/vnd.rn-realsystem-rmx | .rnx | application/vnd.rn-realplayer |
| .rp | image/vnd.rn-realpix | .rpm | audio/x-pn-realaudio-plugin |
| .rsml | application/vnd.rn-rsml | .rt | text/vnd.rn-realtext |
| .rtf | application/msword | .rtf | application/x-rtf |
| .rv | video/vnd.rn-realvideo | .sam | application/x-sam |
| .sat | application/x-sat | .sdp | application/sdp |
| .sdw | application/x-sdw | .sit | application/x-stuffit |
| .slb | application/x-slb | .sld | application/x-sld |
| .slk | drawing/x-slk | .smi | application/smil |
| .smil | application/smil | .smk | application/x-smk |
| .snd | audio/basic | .sol | text/plain |
| .sor | text/plain | .spc | application/x-pkcs7-certificates |
| .spl | application/futuresplash | .spp | text/xml |
| .ssm | application/streamingmedia | .sst | application/vnd.ms-pki.certstore |
| .stl | application/vnd.ms-pki.stl | .stm | text/html |
| .sty | application/x-sty | .svg | text/xml |
| .swf | application/x-shockwave-flash | .tdf | application/x-tdf |
| .tg4 | application/x-tg4 | .tga | application/x-tga |
| .tif | image/tiff | .tif | application/x-tif |
| .tiff | image/tiff | .tld | text/xml |
| .top | drawing/x-top | .torrent | application/x-bittorrent |
| .tsd | text/xml | .txt | text/plain |
| .uin | application/x-icq | .uls | text/iuls |
| .vcf | text/x-vcard | .vda | application/x-vda |
| .vdx | application/vnd.visio | .vml | text/xml |
| .vpg | application/x-vpeg005 | .vsd | application/vnd.visio |
| .vsd | application/x-vsd | .vss | application/vnd.visio |
| .vst | application/vnd.visio | .vst | application/x-vst |
| .vsw | application/vnd.visio | .vsx | application/vnd.visio |
| .vtx | application/vnd.visio | .vxml | text/xml |
| .wav | audio/wav | .wax | audio/x-ms-wax |
| .wb1 | application/x-wb1 | .wb2 | application/x-wb2 |
| .wb3 | application/x-wb3 | .wbmp | image/vnd.wap.wbmp |
| .wiz | application/msword | .wk3 | application/x-wk3 |
| .wk4 | application/x-wk4 | .wkq | application/x-wkq |
| .wks | application/x-wks | .wm | video/x-ms-wm |
| .wma | audio/x-ms-wma | .wmd | application/x-ms-wmd |
| .wmf | application/x-wmf | .wml | text/vnd.wap.wml |
| .wmv | video/x-ms-wmv | .wmx | video/x-ms-wmx |
| .wmz | application/x-ms-wmz | .wp6 | application/x-wp6 |
| .wpd | application/x-wpd | .wpg | application/x-wpg |
| .wpl | application/vnd.ms-wpl | .wq1 | application/x-wq1 |
| .wr1 | application/x-wr1 | .wri | application/x-wri |
| .wrk | application/x-wrk | .ws | application/x-ws |
| .ws2 | application/x-ws | .wsc | text/scriptlet |
| .wsdl | text/xml | .wvx | video/x-ms-wvx |
| .xdp | application/vnd.adobe.xdp | .xdr | text/xml |
| .xfd | application/vnd.adobe.xfd | .xfdf | application/vnd.adobe.xfdf |
| .xhtml | text/html | .xls | application/vnd.ms-excel |
| .xls | application/x-xls | .xlw | application/x-xlw |
| .xml | text/xml | .xpl | audio/scpls |
| .xq | text/xml | .xql | text/xml |
| .xquery | text/xml | .xsd | text/xml |
| .xsl | text/xml | .xslt | text/xml |
| .xwd | application/x-xwd | .x_b | application/x-x_b |
| .sis | application/vnd.symbian.install | .sisx | application/vnd.symbian.install |
| .x_t | application/x-x_t | .ipa | application/vnd.iphone |
| .apk | application/vnd.android.package-archive | .xap | application/x-silverlight-app |
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/295460.html
標籤:其他
上一篇:豆瓣評分預測(如何用自己的資料集進行文本分類)——基于pytorch的 BERT中文文本分類,超詳細教程必會!!!
