京山游俠

          專注技術,拒絕扯淡
          posts - 50, comments - 868, trackbacks - 0, articles - 0
            BlogJava :: 首頁 :: 新隨筆 :: 聯系 :: 聚合  :: 管理

          本篇討論的問題是對項目中遇到的難題進行技術穿刺。

          做過項目的人都知道,在構思完一個項目的功能之后,緊接著的事情就是考慮這些構思的功能如何實現,對于自己不熟悉的領域,要進行技術穿刺。我的穿刺方法為先查找有無比較好的開源組件可用,如果沒有,就查找相關的文檔,自己編寫和測試代碼。

          在這一篇,我主要解決三個問題。
          1、解決字符串加密的問題,在前面一篇中,我們設計用戶模塊的時候,準備將用戶的密碼字段以MD5加密的方式保存,因此,這里需要寫一個對字符串加密生成MD5字符串的方法;
          2、解決生成圖像縮略圖和生成驗證碼的問題;
          3、解決url重寫的問題,之所以要用到url重寫,主要是為了讓用戶在訪問自己的主頁時,可以使用http://www.xkland.com/username或者http://username.xkland.com這樣的形式,而不是像http://www.xkland.com/index.jsp?username=xxx這樣難看的形式。

          需要說明的是,要解決上面的三個問題,不是沒有開源的東西可用,而是我覺得每次都要整合不同的組件是在是太麻煩,而我們需要的功能也不是很復雜,我們不需要太通用的東西,只要能夠解決這里特定的問題就行了,因此不如自己動手實現,同時還可以獲得技術上的提高。

          首先來看看MD5加密的問題,JDK中本來提供有數據加密的支持,其中java.security.MessageDigest類就可以實現MD5的加密,但是,加密后生成的數據是byte[]類型的,這里只需要寫一個方法將它轉換為字符串就行,代碼如下:

          package ?com.xkland.util;

          import ?java.security.MessageDigest;
          import ?java.lang.NullPointerException;
          import ?java.security.NoSuchAlgorithmException;

          public ? class ?StringUtil? {
          ?
          public ? static ? char []?num_chars? = ? new ? char [] { ' 0 ' ,
          ??
          ' 1 ' , ' 2 ' , ' 3 ' , ' 4 ' , ' 5 ' , ' 6 ' , ' 7 ' , ' 8 ' ,
          ??
          ' 9 ' , ' A ' , ' B ' , ' C ' , ' D ' , ' E ' , ' F ' }
          ;
          ?
          ?
          public ? static ?String?toMD5String(String?input)?
          ?
          throws ?NullPointerException,NoSuchAlgorithmException {
          ??
          if (input? == ? null )? throw ? new ?NullPointerException();
          ??
          char []?output? = ? new ? char [ 32 ];
          ??MessageDigest?md?
          = ?MessageDigest.getInstance( " MD5 " );
          ??
          byte []?by? = ?md.digest(input.getBytes());
          ??
          for ( int ?i = 0 ;i < by.length;i ++ ) {
          ???output[
          2 * i]? = ?num_chars[?(by[i]? & ? 0xf0 )? >> ? 4 ?];
          ???output[
          2 * i + 1 ]? = ?num_chars[?by[i]? & ? 0xf ?];
          ??}

          ??
          return ? new ?String(output);
          ?}

          }


          下面是它的測試用例:

          package ?com.xkland.util;

          import ?junit.framework.TestCase;

          public ? class ?StringUtilTest? extends ?TestCase? {

          ?
          public ? void ?testToMD5String()? {
          ??
          try {
          ???System.out.println(StringUtil.toMD5String(
          " abc " ));
          ??}
          catch (Exception?e) {
          ???
          ??}

          ?}


          }



          運行測試用例,輸出結果為:

          900150983CD24FB0D6963F7D28E17F72


          再來說說關于圖像縮略圖生成的問題,我準備將它設置為一個可以讓Spring管理的類,簡單的說,可以利用Spring的配置文件來設置該類的一些屬性,比如原圖像保存的目錄和目標圖像保存的目錄,生成的縮略圖的大小,生成縮略圖的方式。這里特別需要說明的就是這個生成縮略圖的方式,我們即可以指定它只簡單的執行縮放,也可以指定它進行剪裁以后再縮放。為什么要這么設計,請大家看看如下的效果圖,對于下面這兩張美女圖:
          32.jpg

          33.jpg


          如果我們只通過簡單的縮放來生成縮略圖,那么在網頁上的布局效果為:
          38.JPG

          如果我們通過先剪切后縮放的效果來生成縮略圖,那么在網頁上布局的效果為:
          39.JPG

          可以看到通過第二種方式生成的縮略圖布局要漂亮一些,但是會損失圖片的信息。因此,兩種方式各有優劣。所以在設計的時候就設計為能夠讓用戶靈活配置。

          對于有些網友反映的gif動畫經過縮放以后就不能動了,這個問題的主要原因是因為Java SDK 1.4和1.5版本的ImageIO類只能讀gif格式的文件,而不能寫gif格式的文件,因此,對于gif格式的文件,生成的縮略圖只能用png格式代替,在我的設計中,我準備讓bmp格式的文件也讓png格式代替,因為png格式生成的文件更小,而且也不損失圖片質量。至于Java SDK 1.4和1.5版不支持寫gif格式的文件,可以查看Java文檔,下面是截圖:
          30.JPG

          最新推出的Java SDK 6是可以寫gif格式的文件的,因此如果要解決這個問題,可以使用最新的JDK,下面是文檔截圖:
          31.JPG

          下面是我寫的生成縮略圖和生成驗證碼的ImageUtil類的源代碼:

          package ?com.xkland.util;

          import ?javax.imageio.ImageIO;

          import ?java.awt.image.BufferedImage;
          import ?java.io.File;
          import ?java.awt.Image;
          import ?java.awt.Graphics2D;
          import ?java.util.Random;
          import ?java.awt.Font;
          import ?javax.servlet.http.HttpSession;

          public ? class ?ImageUtil? {
          ????
          private ?String?sourceDir; // 圖片的存放路徑
          ???? private ?String?destinationDir; // 縮略圖的存放路徑
          ???? private ?String?mode; // 生成縮略圖的模式,可選ScaleOnly或ClipAndScale
          ???? private ?String?width; // 縮略圖的寬度
          ???? private ?String?height; // 縮略圖的高度
          ???? private ?String?characterStorage; // 用來生成驗證碼的字符倉庫
          ????
          ????
          // 以下代碼段是為了使用Spring注入屬性
          ???? public ? void ?setCharacterStorage(String?characterStorage)? {
          ????????
          this .characterStorage? = ?characterStorage;
          ????}

          ????
          public ? void ?setDestinationDir(String?destinationDir)? {
          ????????
          this .destinationDir? = ?destinationDir;
          ????}

          ????
          public ? void ?setHeight(String?height)? {
          ????????
          this .height? = ?height;
          ????}

          ????
          public ? void ?setMode(String?mode)? {
          ????????
          this .mode? = ?mode;
          ????}

          ????
          public ? void ?setSourceDir(String?sourceDir)? {
          ????????
          this .sourceDir? = ?sourceDir;
          ????}

          ????
          public ? void ?setWidth(String?width)? {
          ????????
          this .width? = ?width;
          ????}

          ????
          ????
          // 生成縮略圖的方法,默認縮略圖的文件名和原圖相同,存放路徑不同
          ???? public ? void ?createMicroImage(String?fileName)
          ????
          throws ?Exception {
          ????????
          // 判斷sourceDir的格式是否為以"\"結尾,并生成完整的路徑
          ????????String?sourceFileName;
          ????????String?destinationFileName;
          ????????
          if (sourceDir.lastIndexOf( ' \\ ' )? != ?(sourceDir.length() - 1 )) {
          ????????????sourceFileName?
          = ?sourceDir? + ? " \\ " ? + ?fileName;
          ????????????destinationFileName?
          = ?destinationDir? + ? " \\ " ? + ?fileName;
          ????????}
          else {
          ????????????sourceFileName?
          = ?sourceDir? + ?fileName;
          ????????????destinationFileName?
          = ?destinationDir? + ?fileName;
          ????????}

          ????????
          ????????
          // 創建文件,并判斷原文件是否存在
          ????????File?sourceFile? = ? new ?File(sourceFileName);
          ????????
          if ( ! sourceFile.exists()) {
          ????????????
          throw ? new ?Exception();
          ????????}

          ????????
          // 根據擴展名判斷原文件的格式
          ????????String?extension? = ?fileName.substring(fileName.lastIndexOf( ' . ' ) + 1 );
          ????????
          if ( ! extension.equalsIgnoreCase( " jpg " )? && ? ! extension.equalsIgnoreCase( " bmp " )
          ????????????????
          && ? ! extension.equalsIgnoreCase( " gif " )? && ? ! extension.equalsIgnoreCase( " png " )) {
          ????????????
          throw ? new ?Exception();
          ????????}

          ????????
          ????????
          // 判斷縮略圖的寬度和高度是否正確,如果不能正確解析則拋出異常
          ???????? int ?destinationWidth? = ?Integer.parseInt(width);
          ????????
          int ?destinationHeight? = ?Integer.parseInt(height);
          ????????
          ????????
          // 判斷縮放模式是否正確,如果配置錯誤,則拋出異常
          ???????? if ( ! mode.equalsIgnoreCase( " ScaleOnly " )
          ????????????????
          && ? ! mode.equalsIgnoreCase( " ClipAndScale " )) {
          ????????????
          throw ? new ?Exception();
          ????????}

          ????????
          ????????
          // 讀取圖像文件,并創建BufferedImage對象,如果不能讀取,則拋出異常
          ????????BufferedImage?image? = ? null ;
          ????????image?
          = ?ImageIO.read(sourceFile);
          ????????
          if (image == null ) {
          ????????????
          throw ? new ?Exception();
          ????????}

          ????????
          ????????
          // 獲取原圖像文件的高度和寬度
          ???????? int ?sourceWidth? = ?image.getWidth();
          ????????
          int ?sourceHeight? = ?image.getHeight();
          ????????
          // 生成縮略圖
          ???????? if (mode.equalsIgnoreCase( " ScaleOnly " )) {
          ????????????BufferedImage?destinationImage;
          ????????????
          if (( float )sourceWidth / destinationWidth? > ?( float )sourceHeight / destinationHeight) {
          ????????????????Image?tempImage?
          = ?image.getScaledInstance(destinationWidth,?( int )(destinationWidth * (( float )sourceHeight / sourceWidth)),?Image.SCALE_DEFAULT);
          ????????????????destinationImage?
          = ? new ?BufferedImage(destinationWidth,?( int )(destinationWidth * (( float )sourceHeight / sourceWidth)),BufferedImage.TYPE_INT_RGB);
          ????????????????Graphics2D?graphics?
          = ?destinationImage.createGraphics();
          ????????????????graphics.drawImage(tempImage,
          0 , 0 , null );
          ????????????????
          ????????????}
          else {
          ????????????????Image?tempImage?
          = ?image.getScaledInstance(( int )(destinationHeight * (( float )sourceWidth / sourceHeight)),?destinationHeight,?Image.SCALE_DEFAULT);
          ????????????????destinationImage?
          = ? new ?BufferedImage(( int )(destinationHeight * (( float )sourceWidth / sourceHeight)),?destinationHeight,BufferedImage.TYPE_INT_RGB);
          ????????????????Graphics2D?graphics?
          = ?destinationImage.createGraphics();
          ????????????????graphics.drawImage(tempImage,
          0 , 0 , null );
          ????????????}
          ????
          ????????????
          // ?如果是bmp或者gif,則縮略圖為png格式
          ???????????? if (extension.equalsIgnoreCase( " bmp " ) || extension.equalsIgnoreCase( " gif " )) {
          ????????????????extension?
          = ? " png " ;
          ????????????????destinationFileName?
          = ?destinationFileName.substring( 0 ,?destinationFileName.lastIndexOf( ' . ' ))? + ? " . " ? + ?extension;
          ????????????}

          ????????????File?destinationFile?
          = ? new ?File(destinationFileName);
          ????????????ImageIO.write(destinationImage,?extension,?destinationFile);
          ????????}
          else {
          ????????????BufferedImage?destinationImage;
          ????????????
          if (( float )sourceWidth / destinationWidth? > ?( float )sourceHeight / destinationHeight) {
          ????????????????
          // 先裁減
          ???????????????? int ?x? = ?sourceWidth? - ?( int )(sourceHeight * (( float )destinationWidth / destinationHeight));
          ????????????????Image?clipedImage?
          = ?image.getSubimage(( int )( 0.5 * x),? 0 ,?( int )(sourceHeight * (( float )destinationWidth / destinationHeight)),?sourceHeight);
          ????????????????
          // 后縮放
          ????????????????Image?scaledImage? = ?clipedImage.getScaledInstance(destinationWidth,?destinationHeight,?Image.SCALE_DEFAULT);
          ????????????????destinationImage?
          = ? new ?BufferedImage(destinationWidth,?destinationHeight,BufferedImage.TYPE_INT_RGB);
          ????????????????Graphics2D?graphics?
          = ?destinationImage.createGraphics();
          ????????????????graphics.drawImage(scaledImage,
          0 , 0 , null );
          ????????????????
          ????????????}
          else {
          // ????????????????先裁減
          ???????????????? int ?y? = ?sourceHeight? - ?( int )(sourceWidth * (( float )destinationHeight / destinationWidth));
          ????????????????Image?clipedImage?
          = ?image.getSubimage( 0 ,?( int )( 0.5 * y),?sourceWidth,?( int )(sourceWidth * (( float )destinationHeight / destinationWidth)));
          ????????????????
          // 后縮放
          ????????????????Image?scaledImage? = ?clipedImage.getScaledInstance(destinationWidth,?destinationHeight,?Image.SCALE_DEFAULT);
          ????????????????destinationImage?
          = ? new ?BufferedImage(destinationWidth,?destinationHeight,BufferedImage.TYPE_INT_RGB);
          ????????????????Graphics2D?graphics?
          = ?destinationImage.createGraphics();
          ????????????????graphics.drawImage(scaledImage,
          0 , 0 , null );
          ????????????}
          ????
          ????????????
          // ?如果是bmp或者gif,則縮略圖為png格式
          ???????????? if (extension.equalsIgnoreCase( " bmp " ) || extension.equalsIgnoreCase( " gif " )) {
          ????????????????extension?
          = ? " png " ;
          ????????????????destinationFileName?
          = ?destinationFileName.substring( 0 ,?destinationFileName.lastIndexOf( ' . ' ))? + ? " . " ? + ?extension;
          ????????????}

          ????????????File?destinationFile?
          = ? new ?File(destinationFileName);
          ????????????ImageIO.write(destinationImage,?extension,?destinationFile);
          ????????}

          ????}


          ????
          // 生成驗證碼的方法
          ???? public ?BufferedImage?createValidateImage(HttpSession?session) {
          ????????BufferedImage?validateImage?
          = ? new ?BufferedImage( 80 , 20 ,BufferedImage.TYPE_INT_RGB);
          ????????Graphics2D?graphics?
          = ?validateImage.createGraphics();
          ????????
          ????????
          // 從characterStorage中隨機抽取四個字符生成驗證碼
          ???????? int ?length? = ?characterStorage.length();
          ????????
          char []?chars? = ? new ? char [ 4 ];
          ????????Random?rand?
          = ? new ?Random();
          ????????
          for ( int ?i = 0 ;?i < 4 ;?i ++ ) {
          ????????????
          int ?index? = ?rand.nextInt(length);
          ????????????chars[i]?
          = ?characterStorage.charAt(index);
          ????????}

          ????????String?str?
          = ? new ?String(chars);
          ????????
          // 將字符串保存到Session中,以便于驗證
          ????????session.setAttribute( " validateString " ,?str);
          ????????
          // 畫字符串到圖片中
          ????????graphics.setFont( new ?Font( " 宋體 " ,Font.BOLD, 18 ));
          ????????graphics.drawString(str,?
          2 ,? 16 );
          ????????
          // 隨機畫干擾直線
          ???????? for ( int ?i = 0 ;?i < 5 ;?i ++ ) {
          ????????????
          int ?x1? = ?rand.nextInt( 80 );
          ????????????
          int ?y1? = ?rand.nextInt( 20 );
          ????????????
          int ?x2? = ?rand.nextInt( 80 );
          ????????????
          int ?y2? = ?rand.nextInt( 20 );
          ????????????graphics.drawLine(x1,?y1,?x2,?y2);
          ????????}

          ????????
          ????????
          return ?validateImage;
          ????}

          }


          寫得比較倉促,沒有進行重構,所以比較難看一點。下面是測試用例的代碼:

          package ?com.xkland.util;

          import ?junit.framework.TestCase;
          import ?javax.imageio.ImageIO;
          import ?java.awt.image.BufferedImage;
          import ?java.io.File;

          public ? class ?ImageUtilTest? extends ?TestCase? {

          ????
          public ? void ?testCreateMicroImage()? throws ?Exception? {
          ????????ImageUtil?util?
          = ? new ?ImageUtil();
          ????????util.setSourceDir(
          " E:\\ " );
          ????????util.setDestinationDir(
          " F:\\ " );
          ????????util.setWidth(
          " 100 " );
          ????????util.setHeight(
          " 100 " );
          ????????
          ????????
          // 以僅縮放的形式生成縮略圖
          ????????util.setMode( " ScaleOnly " );
          ????????
          // 橫圖像
          ????????util.createMicroImage( " 001.bmp " );
          ????????
          // 豎圖像
          ????????util.createMicroImage( " 002.jpg " );
          ????????
          ????????
          // 以先裁減后縮放的形式生成縮略圖
          ????????util.setDestinationDir( " G:\\ " );
          ????????util.setMode(
          " ClipAndScale " );
          ????????
          // 橫圖像
          ????????util.createMicroImage( " 001.bmp " );
          ????????
          // 豎圖像
          ????????util.createMicroImage( " 002.jpg " );
          ????}

          ????
          ????
          public ? void ?testCreateValidateImage()? throws ?Exception {
          ????????ImageUtil?util?
          = ? new ?ImageUtil();
          ????????util.setCharacterStorage(
          " ABCDEFGHIJKLMNOPQRSTUVWXYZ北冥有魚其名為鯤鯤之大不知其幾千里也化而為鳥其名為鵬鵬之背不知其幾千里也怒而飛其翼若垂天之云是鳥也海運則將徙于南冥南冥者天池也 " );
          ????????BufferedImage?image?
          = ?util.createValidateImage();
          ????????ImageIO.write(image,?
          " jpg " ,? new ?File( " F:\\validateImage.jpg " ));
          ????}


          }


          運行該測試用例,可以成功的生成縮略圖,并且可以生成驗證碼,生成的驗證碼如下圖:
          40.jpg
          把以上代碼再修改再完善,就可以創建更漂亮一點的圖形了。

          為了把上面這個ImageUtil類讓SpringSide管理起來,并進行靈活的配置,可以在src\main\resources\spring目錄下建立beans.xml文件,并如下配置:

          <? xml?version="1.0"?encoding="UTF-8" ?>
          <! DOCTYPE?beans?PUBLIC?"-//SPRING//DTD?BEAN?2.0//EN"?"http://www.springframework.org/dtd/spring-beans-2.0.dtd" >
          < beans >
          ????
          < bean? id ="imageUtil" ?class ="com.xkland.util.ImageUtil" >
          ????????
          < property? name ="sourceDir" >
          ????????????
          < value > E:\ </ value >
          ????????
          </ property >
          ????????
          < property? name ="destinationDir" >
          ????????????
          < value > F:\ </ value >
          ????????
          </ property >
          ????????
          < property? name ="width" >
          ????????????
          < value > 100 </ value >
          ????????
          </ property >
          ????????
          < property? name ="height" >
          ????????????
          < value > 100 </ value >
          ????????
          </ property >
          ????????
          < property? name ="mode" >
          ????????????
          < value > ScaleOnly </ value >
          ????????
          </ property >
          ????????
          < property? name ="characterStorage" >
          ????????????
          < value > ABCDEFGHIJKLMNOPQRSTUVWXYZ北冥有魚其名為鯤鯤之大不知其幾千里也化而為鳥其名為鵬鵬之背不知其幾千里也怒而飛其翼若垂天之云是鳥也海運則將徙于南冥南冥者天池也 </ value >
          ????????
          </ property >
          ????
          </ bean >
          </ beans >


          最后,我們再來看看url重寫的問題。俗話說得好:“會者不難,難者不會”,剛開始我為了實現文章開頭所說的url重寫功能,嘗試采用的是配置Servlet映射的方法,但是怎么都不成功,后來才想到使用Filter來實現。有時候開源的東西會直接影響人的思路,比如Struts 1.x采用的就是配置Servlet映射的方法,而到了2.0,也改成Filter了。

          在Filter中實現url重寫比較簡單,無非就是分析字符串和替換字符串,這里我就不列代碼了。只有想不到,沒有做不到,想辦法實現我們設計的功能,這便是技術穿刺的作用。


          評論

          # zsvsdxcw  回復  更多評論   

          2007-01-11 23:46 by zsvsdxcw
          [URL=http://umclazts.com]vuxovteu[/URL] ezyquqvk http://vngphjrs.com zsjhmgtc kewcuwpw <a href="http://kdwsxrkk.com">quixkxnw</a>

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 00:18 by zz[匿名]
          你這跑題可跑的也太遠了....這節跟springside沒啥關系

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 00:31 by billbai
          看到你說到的:將它設置為一個可以讓Spring管理的類,簡單的說,可以利用Spring的配置文件來設置該類的一些屬性,比如原圖像保存的目錄和目標圖像保存的目錄,生成的縮略圖的大小,生成縮略圖的方式。這里特別需要說明的就是這個生成縮略圖的方式,我們即可以指定它只簡單的執行縮放,也可以指定它進行剪裁以后再縮放。

          功能上我都做到了,失真的問題我也解決了。就是更改下你說的讓Sping來管理,然后還可以配置。可以交流下 QQ:285853807
          可以的話交換個鏈接 我已經把你加了。

          # eexlkmwn  回復  更多評論   

          2007-01-12 00:47 by eexlkmwn
          egqhsqbd http://uricvuds.com nbbwhpcs mdfiyxlr <a href="http://sdldxhfm.com">llbctmzr</a> [URL=http://ucuflorh.com]xoihbumf[/URL]

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 10:42 by 摟住sb
          temp = (java.lang.Integer.toHexString(b[n] & 0XFF));

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 13:32 by 海邊沫沫
          @billbai
          好的,我已經加你為好友了。

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 13:39 by 海邊沫沫
          @摟住sb
          Integer.toHexString雖好,但是存在以下問題:
          1、生成的字符串不一定都是兩個字符,比如15,轉換為HexString后為"F",而不是"0F",這樣會影響我們生成的MD5String的長度,使其不是32個字符
          2、Integer.toHexString只能接受int參數,所以需要把byte類型轉化為int類型,影響效率
          3、你使用Integer.toHexString后,生成了16個字符串,然后把這16個字符串連接成為一個字符串,更加影響效率;我上面的方法先生成的是32個字符,然后構造字符串,比你的方法快多了

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-12 20:59 by 海邊沫沫
          @zz[匿名]
          怎么會沒有關系呢,在SpringSide的論壇上可是有人提這樣的問題哦。
          框架不是萬能的嘛,我們平時使用開源框架而受益,當然也應該發揮自己的力量也做一點貢獻啦。說不定我上面的代碼有一天被SpringSide吸收了呢?當然,它們專業的團隊肯定會做的更好。

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-01-17 00:59 by suwu
          非常好,獲益良多

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-02-02 11:25 by raptor
          太棒了,作者的文章太棒了,解決了我的大問題,實在是太感謝了,感謝

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-02-27 10:40 by zj
          寫得很好!

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-07-30 16:20 by 小白之家
          好東西,不能不回帖

          # re: SpringSide開發實戰(五):兵馬未動,糧草先行  回復  更多評論   

          2007-08-14 14:46 by 同聲翻譯公司
          您好,我們公司是一家中國境內的專業翻譯公司,從事各專業翻譯服務,包括筆譯、口譯、同聲傳譯和同聲傳譯設備租賃等。我們需要招聘兼職翻譯、同傳譯員和外籍英文校對人員,不知道是否有時間。

          希望有機會合作.
          郭先生

          北京華譯網翻譯公司
          中國專家翻譯網
          地址:北京海淀區太陽園17號樓405室(北三環西路大鐘寺東側)郵編:100098
          電話:010-82115891 82115892 傳真010-82130386
          上海:上海漕溪北路38號20G (東方商廈后面實業公寓南樓)
          電話:021-34240860 34240925 傳真:021-34240925

          同傳租賃 同聲傳譯翻譯公司 同聲傳譯設備租賃 同聲傳譯 同聲傳譯設備租賃 同傳租賃 北京同傳設備租賃 上海同聲傳譯設備租賃 同聲傳譯
          主站蜘蛛池模板: 桂林市| 清河县| 香港 | 屏山县| 宕昌县| 陇南市| 阿克陶县| 辽中县| 中西区| 资阳市| 莆田市| 察哈| 台州市| 察隅县| 彰武县| 铅山县| 黔西县| 洱源县| 四平市| 远安县| 中山市| 拉萨市| 潜江市| 福贡县| 广水市| 阜南县| 安图县| 临城县| 文山县| 广元市| 淅川县| 驻马店市| 颍上县| 林芝县| 四子王旗| 海宁市| 得荣县| 东乡族自治县| 长顺县| 依兰县| 当涂县|